66from azure .common import AzureException
77from dateutil import parser
88
9+ from azure .storage .common ._http import HTTPResponse
10+
911try :
1012 from xml .etree import cElementTree as ETree
1113except ImportError :
3638 ResourceProperties ,
3739 BlobPrefix ,
3840 AccountInformation ,
39- UserDelegationKey ,
40- )
41+ UserDelegationKey , BatchSubResponse )
4142from ._encryption import _decrypt_blob
4243from azure .storage .common .models import _list
4344from azure .storage .common ._error import (
4445 _validate_content_match ,
4546 _ERROR_DECRYPTION_FAILURE ,
4647)
48+ from io import BytesIO
49+
50+ _HTTP_LINE_ENDING = "\r \n "
51+
52+ def _parse_cpk_headers (response , properties ):
53+ server_encrypted = response .headers .get ('x-ms-request-server-encrypted' )
54+ if server_encrypted is not None :
55+ properties .request_server_encrypted = _bool (server_encrypted )
56+
57+ properties .encryption_key_sha256 = response .headers .get ('x-ms-encryption-key-sha256' )
4758
4859
4960def _parse_base_properties (response ):
@@ -53,6 +64,7 @@ def _parse_base_properties(response):
5364 resource_properties = ResourceProperties ()
5465 resource_properties .last_modified = parser .parse (response .headers .get ('last-modified' ))
5566 resource_properties .etag = response .headers .get ('etag' )
67+ _parse_cpk_headers (response , resource_properties )
5668
5769 return resource_properties
5870
@@ -65,6 +77,7 @@ def _parse_page_properties(response):
6577 put_page .last_modified = parser .parse (response .headers .get ('last-modified' ))
6678 put_page .etag = response .headers .get ('etag' )
6779 put_page .sequence_number = _to_int (response .headers .get ('x-ms-blob-sequence-number' ))
80+ _parse_cpk_headers (response , put_page )
6881
6982 return put_page
7083
@@ -78,6 +91,7 @@ def _parse_append_block(response):
7891 append_block .etag = response .headers .get ('etag' )
7992 append_block .append_offset = _to_int (response .headers .get ('x-ms-blob-append-offset' ))
8093 append_block .committed_block_count = _to_int (response .headers .get ('x-ms-blob-committed-block-count' ))
94+ _parse_cpk_headers (response , append_block )
8195
8296 return append_block
8397
@@ -290,7 +304,7 @@ def _convert_xml_to_blob_list(response):
290304 <RemainingRetentionDays>int</RemainingRetentionDays>
291305 <Creation-Time>date-time-value</Creation-Time>
292306 </Properties>
293- <Metadata>
307+ <Metadata>
294308 <Name>value</Name>
295309 </Metadata>
296310 </Blob>
@@ -392,7 +406,7 @@ def _convert_xml_to_blob_name_list(response):
392406 <RemainingRetentionDays>int</RemainingRetentionDays>
393407 <Creation-Time>date-time-value</Creation-Time>
394408 </Properties>
395- <Metadata>
409+ <Metadata>
396410 <Name>value</Name>
397411 </Metadata>
398412 </Blob>
@@ -475,19 +489,19 @@ def _convert_xml_to_page_ranges(response):
475489 '''
476490 <?xml version="1.0" encoding="utf-8"?>
477491 <PageList>
478- <PageRange>
479- <Start>Start Byte</Start>
480- <End>End Byte</End>
481- </PageRange>
482- <ClearRange>
483- <Start>Start Byte</Start>
484- <End>End Byte</End>
485- </ClearRange>
486- <PageRange>
487- <Start>Start Byte</Start>
488- <End>End Byte</End>
489- </PageRange>
490- </PageList>
492+ <PageRange>
493+ <Start>Start Byte</Start>
494+ <End>End Byte</End>
495+ </PageRange>
496+ <ClearRange>
497+ <Start>Start Byte</Start>
498+ <End>End Byte</End>
499+ </ClearRange>
500+ <PageRange>
501+ <Start>Start Byte</Start>
502+ <End>End Byte</End>
503+ </PageRange>
504+ </PageList>
491505 '''
492506 if response is None or response .body is None :
493507 return None
@@ -554,3 +568,83 @@ def _convert_xml_to_user_delegation_key(response):
554568 delegation_key .value = key_element .findtext ('Value' )
555569
556570 return delegation_key
571+
572+
573+ def _ingest_batch_response (batch_response , batch_sub_requests ):
574+ """
575+ Takes the response to a batch request and parses the response into the separate responses.
576+
577+ :param :class:`~azure.storage.common._http.HTTPResponse` batch_response:
578+ batchResponse The response of the HTTP batch request generated by this object.
579+ :return: sub-responses parsed from batch HTTP response
580+ :rtype: list of :class:`~azure.storage.common._http.HTTPResponse`
581+ """
582+ parsed_batch_sub_response_list = []
583+
584+ # header value format: `multipart/mixed; boundary=<delimiter>`
585+ response_delimiter = batch_response .headers .get ('content-type' ).split ("=" )[1 ]
586+
587+ response_body = batch_response .body .decode ('utf-8' )
588+
589+ # split byte[] on the "substring" "--<delim>\r\n"
590+ sub_response_list = response_body .split ("--" + response_delimiter + _HTTP_LINE_ENDING )
591+
592+ # strip final, slightly different delim "\r\n--<delim>--" off last entry
593+ sub_response_list [len (sub_response_list ) - 1 ] = \
594+ sub_response_list [len (sub_response_list ) - 1 ].split (_HTTP_LINE_ENDING + "--" + response_delimiter + "--" )[0 ]
595+
596+ for sub_response in sub_response_list :
597+ if len (sub_response ) != 0 :
598+ http_response = _parse_sub_response_to_http_response (sub_response )
599+ is_successful = 200 <= http_response .status < 300
600+ index_of_sub_request = _to_int (http_response .headers .get ('Content-ID' ))
601+ batch_sub_request = batch_sub_requests [index_of_sub_request ]
602+
603+ parsed_batch_sub_response_list .append (BatchSubResponse (is_successful , http_response , batch_sub_request ))
604+
605+ return parsed_batch_sub_response_list
606+
607+
608+ def _parse_sub_response_to_http_response (sub_response ):
609+ """
610+ Header: Value (1 or more times)
611+
612+ HTTP/<version> <statusCode> <statusName>
613+ Header: Value (1 or more times)
614+
615+ body (if any)
616+
617+ :param sub_response:
618+ The raw bytes of this sub-response.
619+ :return: An HttpResponse object.
620+ """
621+
622+ empty_line = _HTTP_LINE_ENDING .encode ('utf-8' )
623+ num_empty_lines = 0
624+ batch_http_sub_response = HTTPResponse (None , '' , dict (), b'' )
625+ try :
626+ body_stream = BytesIO ()
627+ body_stream .write (sub_response .encode ('utf-8' ))
628+ body_stream .seek (0 )
629+
630+ while True :
631+ line = body_stream .readline ()
632+ if line == b'' :
633+ return batch_http_sub_response
634+
635+ if line .startswith ("HTTP" .encode ('utf-8' )):
636+ batch_http_sub_response .status = _to_int (line .decode ('utf-8' ).split (" " )[1 ])
637+ elif line == empty_line :
638+ num_empty_lines += 1
639+ elif line .startswith ("x-ms-error-code" .encode ('utf-8' )):
640+ batch_http_sub_response .message = line .decode ('utf-8' ).split (": " )[1 ].rstrip ()
641+ elif num_empty_lines is 2 :
642+ batch_http_sub_response .body += line
643+ else :
644+ header = line .decode ('utf-8' ).split (": " )[0 ]
645+ value = line .decode ('utf-8' ).split (": " )[1 ].rstrip ()
646+ batch_http_sub_response .headers [header ] = value
647+ finally :
648+ body_stream .close ()
649+
650+ return batch_http_sub_response
0 commit comments