Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-blob/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/storage/azure-storage-blob",
"Tag": "python/storage/azure-storage-blob_863b753fbb"
"Tag": "python/storage/azure-storage-blob_275000b78a"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import re
from typing import List, Tuple
from urllib.parse import unquote, urlparse
from functools import cmp_to_key

try:
from yarl import URL
Expand All @@ -27,6 +28,66 @@
logger = logging.getLogger(__name__)


table_lv0 = [
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725,
0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e,
0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51,
0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9,
0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25,
0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99,
0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0,
]

table_lv4 = [
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
]

def compare(lhs: str, rhs: str) -> int: # pylint:disable=too-many-return-statements
tables = [table_lv0, table_lv4]
curr_level, i, j, n = 0, 0, 0, len(tables)
lhs_len = len(lhs)
rhs_len = len(rhs)
while curr_level < n:
if curr_level == (n - 1) and i != j:
if i > j:
return -1
if i < j:
return 1
return 0

w1 = tables[curr_level][ord(lhs[i])] if i < lhs_len else 0x1
w2 = tables[curr_level][ord(rhs[j])] if j < rhs_len else 0x1

if w1 == 0x1 and w2 == 0x1:
i = 0
j = 0
curr_level += 1
elif w1 == w2:
i += 1
j += 1
elif w1 == 0:
i += 1
elif w2 == 0:
j += 1
else:
if w1 < w2:
return -1
if w1 > w2:
return 1
return 0
return 0


# wraps a given exception with the desired exception type
def _wrap_exception(ex, desired_type):
msg = ""
Expand All @@ -36,8 +97,6 @@ def _wrap_exception(ex, desired_type):

# This method attempts to emulate the sorting done by the service
def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
# Define the custom alphabet for weights
custom_weights = "-!#$%&*.^_|~+\"\'(),/`~0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz{}"

# Build dict of tuples and list of keys
header_dict = {}
Expand All @@ -46,9 +105,8 @@ def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str
header_dict[k] = v
header_keys.append(k)

# Sort according to custom defined weights
try:
header_keys = sorted(header_keys, key=lambda word: [custom_weights.index(c) for c in word])
header_keys = sorted(header_keys, key=cmp_to_key(compare))
except ValueError as exc:
raise ValueError("Illegal character encountered when sorting headers.") from exc

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3124,4 +3124,45 @@ def test_header_metadata_sort_in_upload_blob(self, **kwargs):
# Act
blob_client.upload_blob(data, length=len(data), metadata=metadata)

@BlobPreparer()
@recorded_by_proxy
def test_header_metadata_sort_in_upload_blob_translation(self, **kwargs):
storage_account_name = kwargs.pop("storage_account_name")
storage_account_key = kwargs.pop("storage_account_key")

self._setup()
data = b'hello world'
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)
try:
container_client = bsc.create_container(self.container_name)
except:
container_client = bsc.get_container_client(self.container_name)
blob_client = container_client.get_blob_client('blob1')

# Hand-picked metadata examples that sorted incorrectly with our previous implementation.
metadata = {
'test': 'val',
'test-': 'val',
'test--': 'val',
'test-_': 'val',
'test_-': 'val',
'test__': 'val',
'test-a': 'val',
'test-A': 'val',
'test-_A': 'val',
'test_a': 'val',
'test_Z': 'val',
'test_a_': 'val',
'test_a-': 'val',
'test_a-_': 'val',
}

# Act
# If we hit invalid metadata error, that means we have successfully sorted headers properly to pass auth error
with pytest.raises(HttpResponseError) as e:
blob_client.upload_blob(data, length=len(data), metadata=metadata)

# Assert
assert StorageErrorCode.invalid_metadata == e.value.error_code

# ------------------------------------------------------------------------------
Original file line number Diff line number Diff line change
Expand Up @@ -3092,4 +3092,69 @@ async def test_header_metadata_sort_in_upload_blob(self, **kwargs):
# Act
await blob_client.upload_blob(data, length=len(data), metadata=metadata)

@BlobPreparer()
@recorded_by_proxy_async
async def test_header_metadata_sort_in_upload_blob(self, **kwargs):
storage_account_name = kwargs.pop("storage_account_name")
storage_account_key = kwargs.pop("storage_account_key")

self._setup()
data = b'hello world'
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)
try:
container_client = await bsc.create_container(self.container_name)
except:
container_client = bsc.get_container_client(self.container_name)
blob_client = container_client.get_blob_client('blob1')

# Hand-picked metadata examples as Python & service don't sort '_' with the same weight
metadata = {'a0': 'a', 'a1': 'a', 'a2': 'a', 'a3': 'a', 'a4': 'a', 'a5': 'a', 'a6': 'a', 'a7': 'a', 'a8': 'a',
'a9': 'a', '_': 'a', '_a': 'a', 'a_': 'a', '__': 'a', '_a_': 'a', 'b': 'a', 'c': 'a', 'y': 'a',
'z': 'z_', '_z': 'a', '_F': 'a', 'F': 'a', 'F_': 'a', '_F_': 'a', '__F': 'a', '__a': 'a', 'a__': 'a'
}

# Act
await blob_client.upload_blob(data, length=len(data), metadata=metadata)

@BlobPreparer()
@recorded_by_proxy_async
async def test_header_metadata_sort_in_upload_blob_translation(self, **kwargs):
storage_account_name = kwargs.pop("storage_account_name")
storage_account_key = kwargs.pop("storage_account_key")

self._setup()
data = b'hello world'
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key)
try:
container_client = await bsc.create_container(self.container_name)
except:
container_client = bsc.get_container_client(self.container_name)
blob_client = container_client.get_blob_client('blob1')

# Hand-picked metadata examples that sorted incorrectly with our previous implementation.
metadata = {
'test': 'val',
'test-': 'val',
'test--': 'val',
'test-_': 'val',
'test_-': 'val',
'test__': 'val',
'test-a': 'val',
'test-A': 'val',
'test-_A': 'val',
'test_a': 'val',
'test_Z': 'val',
'test_a_': 'val',
'test_a-': 'val',
'test_a-_': 'val',
}

# Act
# If we hit invalid metadata error, that means we have successfully sorted headers properly to pass auth error
with pytest.raises(HttpResponseError) as e:
await blob_client.upload_blob(data, length=len(data), metadata=metadata)

# Assert
assert StorageErrorCode.invalid_metadata == e.value.error_code

# ------------------------------------------------------------------------------
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import re
from typing import List, Tuple
from urllib.parse import unquote, urlparse
from functools import cmp_to_key

try:
from yarl import URL
Expand All @@ -27,6 +28,66 @@
logger = logging.getLogger(__name__)


table_lv0 = [
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725,
0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e,
0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51,
0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9,
0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25,
0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99,
0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0,
]

table_lv4 = [
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
]

def compare(lhs: str, rhs: str) -> int: # pylint:disable=too-many-return-statements
tables = [table_lv0, table_lv4]
curr_level, i, j, n = 0, 0, 0, len(tables)
lhs_len = len(lhs)
rhs_len = len(rhs)
while curr_level < n:
if curr_level == (n - 1) and i != j:
if i > j:
return -1
if i < j:
return 1
return 0

w1 = tables[curr_level][ord(lhs[i])] if i < lhs_len else 0x1
w2 = tables[curr_level][ord(rhs[j])] if j < rhs_len else 0x1

if w1 == 0x1 and w2 == 0x1:
i = 0
j = 0
curr_level += 1
elif w1 == w2:
i += 1
j += 1
elif w1 == 0:
i += 1
elif w2 == 0:
j += 1
else:
if w1 < w2:
return -1
if w1 > w2:
return 1
return 0
return 0


# wraps a given exception with the desired exception type
def _wrap_exception(ex, desired_type):
msg = ""
Expand All @@ -36,8 +97,6 @@ def _wrap_exception(ex, desired_type):

# This method attempts to emulate the sorting done by the service
def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
# Define the custom alphabet for weights
custom_weights = "-!#$%&*.^_|~+\"\'(),/`~0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz{}"

# Build dict of tuples and list of keys
header_dict = {}
Expand All @@ -46,9 +105,8 @@ def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str
header_dict[k] = v
header_keys.append(k)

# Sort according to custom defined weights
try:
header_keys = sorted(header_keys, key=lambda word: [custom_weights.index(c) for c in word])
header_keys = sorted(header_keys, key=cmp_to_key(compare))
except ValueError as exc:
raise ValueError("Illegal character encountered when sorting headers.") from exc

Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-file-share/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/storage/azure-storage-file-share",
"Tag": "python/storage/azure-storage-file-share_a600655193"
"Tag": "python/storage/azure-storage-file-share_e105feb7fb"
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@

# This file is used for handwritten extensions to the generated code. Example:
# https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md


def patch_sdk():
pass
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@

# This file is used for handwritten extensions to the generated code. Example:
# https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md


def patch_sdk():
pass
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------

"""Customize generated code here.

Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize
Expand Down
Loading