Validate echoed client request ID

This commit is contained in:
zezha-msft 2019-06-11 00:08:02 -07:00 коммит произвёл Xiaoxi Fu
Родитель 64c61f3dd9
Коммит 219d85e81b
7 изменённых файлов: 132 добавлений и 5 удалений

Просмотреть файл

@ -8,7 +8,7 @@ __author__ = 'Microsoft Corp. <ptvshelp@microsoft.com>'
__version__ = '2.0.1'
# x-ms-version for storage service.
X_MS_VERSION = '2018-11-09'
X_MS_VERSION = '2019-02-02'
# internal configurations, should not be changed
_LARGE_BLOB_UPLOAD_MAX_READ_BUFFER_SIZE = 4 * 1024 * 1024

Просмотреть файл

@ -2,10 +2,17 @@
> See [BreakingChanges](BreakingChanges.md) for a detailed list of API breaks.
## Version XX.XX.XX:
- Support for 2019-02-02 REST version. Please see our REST API documentation and blog for information about the related added features.
- Validate that the echoed client request ID from the service matches the sent one.
## Version 2.0.0:
- Bump version to avoid breaking file/blob/queue v1.5.0.
## Version 1.4.1:
- Added minor helpers for SAS related changes

Просмотреть файл

@ -17,7 +17,7 @@ USER_AGENT_STRING_SUFFIX = '(Python {} {}; {} {})'.format(platform.python_implem
platform.release())
# default values for common package, in case it is used directly
DEFAULT_X_MS_VERSION = '2018-03-28'
DEFAULT_X_MS_VERSION = '2019-02-02'
DEFAULT_USER_AGENT_STRING = '{}None {}'.format(USER_AGENT_STRING_PREFIX, USER_AGENT_STRING_SUFFIX)
# Live ServiceClient URLs
@ -49,3 +49,4 @@ _ENCRYPTION_PROTOCOL_V1 = '1.0'
_AUTHORIZATION_HEADER_NAME = 'Authorization'
_COPY_SOURCE_HEADER_NAME = 'x-ms-copy-source'
_REDACTED_VALUE = 'REDACTED'
_CLIENT_REQUEST_ID_HEADER_NAME = 'x-ms-client-request-id'

Просмотреть файл

@ -34,6 +34,7 @@ from .models import (
from ._common_conversion import (
_str,
)
from ._constants import _CLIENT_REQUEST_ID_HEADER_NAME
def _to_utc_datetime(value):
@ -62,7 +63,7 @@ def _update_request(request, x_ms_version, user_agent_string):
# append addtional headers based on the service
request.headers['x-ms-version'] = x_ms_version
request.headers['User-Agent'] = user_agent_string
request.headers['x-ms-client-request-id'] = str(uuid.uuid1())
request.headers[_CLIENT_REQUEST_ID_HEADER_NAME] = str(uuid.uuid1())
# If the host has a path component (ex local storage), move it
path = request.host.split('/', 1)

Просмотреть файл

@ -24,6 +24,7 @@ from ._constants import (
_AUTHORIZATION_HEADER_NAME,
_REDACTED_VALUE,
_COPY_SOURCE_HEADER_NAME,
_CLIENT_REQUEST_ID_HEADER_NAME,
)
from ._error import (
_ERROR_DECRYPTION_FAILURE,
@ -260,6 +261,16 @@ class StorageClient(object):
clean_queries[_QueryStringConstants.SIGNED_SIGNATURE] = _REDACTED_VALUE
return clean_queries
@staticmethod
def _validate_echoed_client_request_id(request, response):
# raise exception if the echoed client request id from the service is not identical to the one we sent
if _CLIENT_REQUEST_ID_HEADER_NAME in response.headers and \
request.headers[_CLIENT_REQUEST_ID_HEADER_NAME] != response.headers[_CLIENT_REQUEST_ID_HEADER_NAME]:
raise AzureException(
"Echoed client request ID: {} does not match sent client request ID: {}. Service request ID: {}".format(
response.headers[_CLIENT_REQUEST_ID_HEADER_NAME], request.headers[_CLIENT_REQUEST_ID_HEADER_NAME],
response.headers['x-ms-request-id']))
def _perform_request(self, request, parser=None, parser_args=None, operation_context=None, expected_errors=None):
'''
Sends the request and return response. Catches HTTPError and hands it
@ -282,7 +293,7 @@ class StorageClient(object):
# Apply common settings to the request
_update_request(request, self._X_MS_VERSION, self._USER_AGENT_STRING)
client_request_id_prefix = str.format("Client-Request-ID={0}", request.headers['x-ms-client-request-id'])
client_request_id_prefix = str.format("Client-Request-ID={0}", request.headers[_CLIENT_REQUEST_ID_HEADER_NAME])
while True:
try:
@ -324,6 +335,9 @@ class StorageClient(object):
if self.response_callback:
self.response_callback(response)
# Validate the client request ID
self._validate_echoed_client_request_id(request, response)
# Set the response context
retry_context.response = response

Просмотреть файл

@ -16,7 +16,9 @@ from tests.testcase import (
StorageTestCase,
record,
)
from azure.storage.common import TokenCredential
from azure.storage.common import TokenCredential, ExponentialRetry
from azure.storage.common._constants import _CLIENT_REQUEST_ID_HEADER_NAME
from azure.common import AzureException
# ------------------------------------------------------------------------------
SERVICES = {
@ -389,6 +391,34 @@ class StorageClientTest(StorageTestCase):
exists = service.exists(name)
self.assertTrue(exists)
@record
def test_client_request_id_echo(self):
# Arrange
service = BlockBlobService(self.account_name, self.account_key, is_emulated=self.settings.IS_EMULATED)
service.retry = ExponentialRetry(max_attempts=1, initial_backoff=1,).retry
name = self.get_resource_name('cont')
# Act make the client request ID slightly different
def callback(response):
response.status = 200
response.headers[_CLIENT_REQUEST_ID_HEADER_NAME] += '1'
service.response_callback = callback
# Assert the client request ID validation is working
with self.assertRaises(AzureException):
service.exists(name)
# Act remove the echoed client request ID
def callback(response):
response.status = 200
del response.headers[_CLIENT_REQUEST_ID_HEADER_NAME]
service.response_callback = callback
# Assert the client request ID validation is not throwing when the ID is not echoed
service.exists(name)
# ------------------------------------------------------------------------------
if __name__ == '__main__':

Просмотреть файл

@ -0,0 +1,74 @@
interactions:
- request:
body: null
headers:
Connection: [keep-alive]
User-Agent: [Azure-Storage/2.0.0-2.0.1 (Python CPython 3.7.0; Darwin 18.6.0)]
x-ms-client-request-id: [b2d411ec-8c17-11e9-9ed6-acde48001122]
x-ms-date: ['Tue, 11 Jun 2019 07:08:22 GMT']
x-ms-version: ['2019-02-02']
method: GET
uri: https://storagename.blob.core.windows.net/cont40380ffd?restype=container
response:
body: {string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>ContainerNotFound</Code><Message>The\
\ specified container does not exist.\nRequestId:9059bb6e-f01e-0002-3d24-202c83000000\n\
Time:2019-06-11T07:08:22.8572973Z</Message></Error>"}
headers:
Content-Length: ['225']
Content-Type: [application/xml]
Date: ['Tue, 11 Jun 2019 07:08:22 GMT']
Server: [Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0]
x-ms-client-request-id: [b2d411ec-8c17-11e9-9ed6-acde48001122]
x-ms-error-code: [ContainerNotFound]
x-ms-request-id: [9059bb6e-f01e-0002-3d24-202c83000000]
x-ms-version: ['2019-02-02']
status: {code: 404, message: The specified container does not exist.}
- request:
body: null
headers:
Connection: [keep-alive]
User-Agent: [Azure-Storage/2.0.0-2.0.1 (Python CPython 3.7.0; Darwin 18.6.0)]
x-ms-client-request-id: [b2d411ec-8c17-11e9-9ed6-acde48001122]
x-ms-date: ['Tue, 11 Jun 2019 07:08:26 GMT']
x-ms-version: ['2019-02-02']
method: GET
uri: https://storagename.blob.core.windows.net/cont40380ffd?restype=container
response:
body: {string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>ContainerNotFound</Code><Message>The\
\ specified container does not exist.\nRequestId:9059bc96-f01e-0002-5824-202c83000000\n\
Time:2019-06-11T07:08:26.7803058Z</Message></Error>"}
headers:
Content-Length: ['225']
Content-Type: [application/xml]
Date: ['Tue, 11 Jun 2019 07:08:25 GMT']
Server: [Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0]
x-ms-client-request-id: [b2d411ec-8c17-11e9-9ed6-acde48001122]
x-ms-error-code: [ContainerNotFound]
x-ms-request-id: [9059bc96-f01e-0002-5824-202c83000000]
x-ms-version: ['2019-02-02']
status: {code: 404, message: The specified container does not exist.}
- request:
body: null
headers:
Connection: [keep-alive]
User-Agent: [Azure-Storage/2.0.0-2.0.1 (Python CPython 3.7.0; Darwin 18.6.0)]
x-ms-client-request-id: [b57a4e2a-8c17-11e9-9ed6-acde48001122]
x-ms-date: ['Tue, 11 Jun 2019 07:08:26 GMT']
x-ms-version: ['2019-02-02']
method: GET
uri: https://storagename.blob.core.windows.net/cont40380ffd?restype=container
response:
body: {string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>ContainerNotFound</Code><Message>The\
\ specified container does not exist.\nRequestId:9059bca5-f01e-0002-6424-202c83000000\n\
Time:2019-06-11T07:08:26.8883049Z</Message></Error>"}
headers:
Content-Length: ['225']
Content-Type: [application/xml]
Date: ['Tue, 11 Jun 2019 07:08:26 GMT']
Server: [Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0]
x-ms-client-request-id: [b57a4e2a-8c17-11e9-9ed6-acde48001122]
x-ms-error-code: [ContainerNotFound]
x-ms-request-id: [9059bca5-f01e-0002-6424-202c83000000]
x-ms-version: ['2019-02-02']
status: {code: 404, message: The specified container does not exist.}
version: 1