def _perform_request()

in azure/multiapi/cosmosdb/v2017_04_17/common/storageclient.py [0:0]


    def _perform_request(self, request, parser=None, parser_args=None, operation_context=None):
        '''
        Sends the request and return response. Catches HTTPError and hands it
        to error handler
        '''
        operation_context = operation_context or _OperationContext()
        retry_context = RetryContext()

        # Apply the appropriate host based on the location mode
        self._apply_host(request, operation_context, retry_context)

        # Apply common settings to the request
        _update_request(request)
        client_request_id_prefix = str.format("Client-Request-ID={0}", request.headers['x-ms-client-request-id'])

        while True:
            try:
                try:
                    # Execute the request callback 
                    if self.request_callback:
                        self.request_callback(request)

                    # Add date and auth after the callback so date doesn't get too old and 
                    # authentication is still correct if signed headers are added in the request 
                    # callback. This also ensures retry policies with long back offs 
                    # will work as it resets the time sensitive headers.
                    _add_date_header(request)
                    self.authentication.sign_request(request)

                    # Set the request context
                    retry_context.request = request

                    # Log the request before it goes out
                    logger.info("%s Outgoing request: Method=%s, Path=%s, Query=%s, Headers=%s.",
                                client_request_id_prefix,
                                request.method,
                                request.path,
                                request.query,
                                str(request.headers).replace('\n', ''))

                    # Perform the request
                    response = self._httpclient.perform_request(request)

                    # Execute the response callback
                    if self.response_callback:
                        self.response_callback(response)

                    # Set the response context
                    retry_context.response = response

                    # Log the response when it comes back
                    logger.info("%s Receiving Response: "
                                "%s, HTTP Status Code=%s, Message=%s, Headers=%s.",
                                client_request_id_prefix,
                                self.extract_date_and_request_id(retry_context),
                                response.status,
                                response.message,
                                str(request.headers).replace('\n', ''))

                    # Parse and wrap HTTP errors in AzureHttpError which inherits from AzureException
                    if response.status >= 300:
                        # This exception will be caught by the general error handler
                        # and raised as an azure http exception
                        _http_error_handler(
                            HTTPError(response.status, response.message, response.headers, response.body))

                    # Parse the response
                    if parser:
                        if parser_args:
                            args = [response]
                            args.extend(parser_args)
                            return parser(*args)
                        else:
                            return parser(response)
                    else:
                        return
                except AzureException as ex:
                    raise ex
                except Exception as ex:
                    if sys.version_info >= (3,):
                        # Automatic chaining in Python 3 means we keep the trace
                        raise AzureException(ex.args[0])
                    else:
                        # There isn't a good solution in 2 for keeping the stack trace 
                        # in general, or that will not result in an error in 3
                        # However, we can keep the previous error type and message
                        # TODO: In the future we will log the trace
                        msg = ""
                        if len(ex.args) > 0:
                            msg = ex.args[0]
                        raise AzureException('{}: {}'.format(ex.__class__.__name__, msg))

            except AzureException as ex:
                # only parse the strings used for logging if logging is at least enabled for CRITICAL
                if logger.isEnabledFor(logging.CRITICAL):
                    exception_str_in_one_line = str(ex).replace('\n', '')
                    status_code = retry_context.response.status if retry_context.response is not None else 'Unknown'
                    timestamp_and_request_id = self.extract_date_and_request_id(retry_context)

                logger.info("%s Operation failed: checking if the operation should be retried. "
                            "Current retry count=%s, %s, HTTP status code=%s, Exception=%s.",
                            client_request_id_prefix,
                            retry_context.count if hasattr(retry_context, 'count') else 0,
                            timestamp_and_request_id,
                            status_code,
                            exception_str_in_one_line)

                # Decryption failures (invalid objects, invalid algorithms, data unencrypted in strict mode, etc)
                # will not be resolved with retries.
                if str(ex) == _ERROR_DECRYPTION_FAILURE:
                    logger.error("%s Encountered decryption failure: this cannot be retried. "
                                 "%s, HTTP status code=%s, Exception=%s.",
                                 client_request_id_prefix,
                                 timestamp_and_request_id,
                                 status_code,
                                 exception_str_in_one_line)
                    raise ex

                # Determine whether a retry should be performed and if so, how 
                # long to wait before performing retry.
                retry_interval = self.retry(retry_context)
                if retry_interval is not None:
                    # Execute the callback
                    if self.retry_callback:
                        self.retry_callback(retry_context)

                    logger.info(
                        "%s Retry policy is allowing a retry: Retry count=%s, Interval=%s.",
                        client_request_id_prefix,
                        retry_context.count,
                        retry_interval)

                    # Sleep for the desired retry interval
                    sleep(retry_interval)
                else:
                    logger.error("%s Retry policy did not allow for a retry: "
                                 "%s, HTTP status code=%s, Exception=%s.",
                                 client_request_id_prefix,
                                 timestamp_and_request_id,
                                 status_code,
                                 exception_str_in_one_line)
                    raise ex
            finally:
                # If this is a location locked operation and the location is not set, 
                # this is the first request of that operation. Set the location to 
                # be used for subsequent requests in the operation.
                if operation_context.location_lock and not operation_context.host_location:
                    operation_context.host_location = {retry_context.location_mode: request.host}