def _QueryServiceState(self, conn, file_length): """Queries service to find out state of given upload. Note that this method really just makes special case use of the fact that the upload service always returns the current start/end state whenever a PUT doesn't complete. Args: conn: HTTPConnection to use for the query. file_length: Total length of the file. Returns: HTTP response from sending request. Raises: ResumableUploadException if problem querying service. """ # Send an empty PUT so that service replies with this resumable # transfer's state. put_headers = { 'Content-Range': (self._BuildContentRangeHeader('*', file_length)), 'Content-Length': '0' } return AWSAuthConnection.make_request(conn, 'PUT', path=self.upload_url_path, auth_path=self.upload_url_path, headers=put_headers, host=self.upload_url_host)
def make_request(self, method, bucket='', key='', headers=None, data='', query_args=None, sender=None, override_num_retries=None, retry_handler=None): if isinstance(bucket, self.bucket_class): bucket = bucket.name if isinstance(key, Key): key = key.name path = self.calling_format.build_path_base(bucket, key) boto.log.debug('path=%s' % path) auth_path = self.calling_format.build_auth_path(bucket, key) boto.log.debug('auth_path=%s' % auth_path) host = self.calling_format.build_host(self.server_name(), bucket) if query_args: path += '?' + query_args boto.log.debug('path=%s' % path) auth_path += '?' + query_args boto.log.debug('auth_path=%s' % auth_path) return AWSAuthConnection.make_request( self, method, path, headers, data, host, auth_path, sender, override_num_retries=override_num_retries, retry_handler=retry_handler)
def _QueryServiceState(self, conn, file_length): """Queries service to find out state of given upload. Note that this method really just makes special case use of the fact that the upload service always returns the current start/end state whenever a PUT doesn't complete. Args: conn: HTTPConnection to use for the query. file_length: Total length of the file. Returns: HTTP response from sending request. Raises: ResumableUploadException if problem querying service. """ # Send an empty PUT so that service replies with this resumable # transfer's state. put_headers = { 'Content-Range': (self._BuildContentRangeHeader('*', file_length)), 'Content-Length': '0' } return AWSAuthConnection.make_request(conn, 'PUT', path=self.upload_url_path, auth_path=self.upload_url_path, headers=put_headers, host=self.upload_url_host)
def set_user_quota(self, uid, tenant='', enabled=False, max_objects=-1, max_size_kb=-1): """获取用户容量限制""" tenant = tenant or self.tenant if tenant: uid = '%s$%s' % (tenant, uid) parameters = {'uid': uid, 'quota-type': 'user', 'quota-scope': 'user'} data = { 'uid': uid, 'quota-scope': 'user', 'enabled': enabled, 'max_objects': max_objects, 'max_size_kb': max_size_kb, } response = AWSAuthConnection.make_request( self.conn, 'PUT', self.admin_endpoint + 'user?quota&' + urllib.parse.urlencode(parameters), data=json.dumps(data), ) return self._handle_response(response)
def make_request(self, verb, resource, headers=None, data='', ok_responses=(200, ), params=None, sender=None, response_headers=None): if headers is None: headers = {} headers['x-amz-glacier-version'] = self.Version uri = '/%s/%s' % (self.account_id, resource) response = AWSAuthConnection.make_request(self, verb, uri, params=params, headers=headers, sender=sender, data=data) if response.status in ok_responses: return GlacierResponse(response, response_headers) else: # create glacier-specific exceptions raise UnexpectedHTTPResponseError(ok_responses, response)
def _query_server_state(self, conn, file_length): """ Queries server to find out state of given upload. Note that this method really just makes special case use of the fact that the upload server always returns the current start/end state whenever a PUT doesn't complete. Returns HTTP response from sending request. Raises ResumableUploadException if problem querying server. """ # Send an empty PUT so that server replies with this resumable # transfer's state. put_headers = {} put_headers["Content-Range"] = self._build_content_range_header("*", file_length) put_headers["Content-Length"] = "0" return AWSAuthConnection.make_request( conn, "PUT", path=self.tracker_uri_path, auth_path=self.tracker_uri_path, headers=put_headers, host=self.tracker_uri_host, )
def _query_server_state(self, conn, file_length): """ Queries server to find out what bytes it currently has. Note that this method really just makes special case use of the fact that the upload server always returns the current start/end state whenever a PUT doesn't complete. Returns (server_start, server_end), where the values are inclusive. For example, (0, 2) would mean that the server has bytes 0, 1, *and* 2. Raises ResumableUploadException if problem querying server. """ # Send an empty PUT so that server replies with this resumable # transfer's state. put_headers = {} put_headers['Content-Range'] = (self._build_content_range_header( '*', file_length)) put_headers['Content-Length'] = '0' resp = AWSAuthConnection.make_request(conn, 'PUT', path=self.tracker_uri_path, headers=put_headers, host=self.tracker_uri_host) if resp.status == 200: return (0, file_length) # Completed upload. if resp.status != 308: # This means the server didn't have any state for the given # upload ID, which can happen (for example) if the caller saved # the tracker URI to a file and then tried to restart the transfer # after that upload ID has gone stale. In that case we need to # start a new transfer (and the caller will then save the new # tracker URI to the tracker file). raise ResumableUploadException( 'Got non-308 response (%s) from server state query' % resp.status, ResumableTransferDisposition.START_OVER) got_valid_response = False range_spec = resp.getheader('range') if range_spec: # Parse 'bytes=<from>-<to>' range_spec. m = re.search('bytes=(\d+)-(\d+)', range_spec) if m: server_start = long(m.group(1)) server_end = long(m.group(2)) got_valid_response = True else: # No Range header, which means the server does not yet have # any bytes. Note that the Range header uses inclusive 'from' # and 'to' values. Since Range 0-0 would mean that the server # has byte 0, omitting the Range header is used to indicate that # the server doesn't have any bytes. return self.SERVER_HAS_NOTHING if not got_valid_response: raise ResumableUploadException( 'Couldn\'t parse upload server state query response (%s)' % str(resp.getheaders()), ResumableTransferDisposition.START_OVER) if conn.debug >= 1: print 'Server has: Range: %d - %d.' % (server_start, server_end) return (server_start, server_end)
def make_request(self, verb, resource, headers=None, data='', ok_responses=(200, )): if headers is None: headers = {} headers['x-amz-glacier-version'] = self.Version uri = '/%s/%s' % (self.account_id, resource) response = AWSAuthConnection.make_request(self, verb, uri, params=params, headers=headers, data=data) if response.status in ok_responses: is_json = response.getheader('Content-Type') == 'application/json' body = json.loads(response.read()) if is_json else response.read() return dict(response.getheaders()), body else: msg = 'Expected %s, got (%d, %s)' % (ok_responses, response.status, response.read()) # create glacier-specific exceptions raise BaseException(msg)
def _query_server_state(self, conn, file_length): """ Queries server to find out what bytes it currently has. Note that this method really just makes special case use of the fact that the upload server always returns the current start/end state whenever a PUT doesn't complete. Returns (server_start, server_end), where the values are inclusive. For example, (0, 2) would mean that the server has bytes 0, 1, *and* 2. Raises ResumableUploadException if problem querying server. """ # Send an empty PUT so that server replies with this resumable # transfer's state. put_headers = {} put_headers['Content-Range'] = ( self._build_content_range_header('*', file_length)) put_headers['Content-Length'] = '0' resp = AWSAuthConnection.make_request(conn, 'PUT', path=self.tracker_uri_path, auth_path=self.tracker_uri_path, headers=put_headers, host=self.tracker_uri_host) if resp.status == 200: return (0, file_length) # Completed upload. if resp.status != 308: # This means the server didn't have any state for the given # upload ID, which can happen (for example) if the caller saved # the tracker URI to a file and then tried to restart the transfer # after that upload ID has gone stale. In that case we need to # start a new transfer (and the caller will then save the new # tracker URI to the tracker file). raise ResumableUploadException( 'Got non-308 response (%s) from server state query' % resp.status, ResumableTransferDisposition.START_OVER) got_valid_response = False range_spec = resp.getheader('range') if range_spec: # Parse 'bytes=<from>-<to>' range_spec. m = re.search('bytes=(\d+)-(\d+)', range_spec) if m: server_start = long(m.group(1)) server_end = long(m.group(2)) got_valid_response = True else: # No Range header, which means the server does not yet have # any bytes. Note that the Range header uses inclusive 'from' # and 'to' values. Since Range 0-0 would mean that the server # has byte 0, omitting the Range header is used to indicate that # the server doesn't have any bytes. return self.SERVER_HAS_NOTHING if not got_valid_response: raise ResumableUploadException( 'Couldn\'t parse upload server state query response (%s)' % str(resp.getheaders()), ResumableTransferDisposition.START_OVER) if conn.debug >= 1: print 'Server has: Range: %d - %d.' % (server_start, server_end) return (server_start, server_end)
def make_request(self, action, path, headers=None, data='', params=None): if params: pairs = [] for key, val in params.iteritems(): if val is None: continue pairs.append(key + '=' + urllib.quote(str(val))) path += '?' + '&'.join(pairs) return AWSAuthConnection.make_request(self, action, path, headers, data)
def make_request(self, action, path, headers=None, data='', params=None): if params: pairs = [] for key, val in params.iteritems(): if val is None: continue pairs.append(key + '=' + urllib.quote(str(val))) path += '?' + '&'.join(pairs) return AWSAuthConnection.make_request(self, action, path, headers, data)
def make_request(self, action, path, headers=None, data="", params=None): if params: pairs = [] for key, val in params.iteritems(): if val is None: continue pairs.append(key + "=" + urllib.quote(str(val))) path += "?" + "&".join(pairs) return AWSAuthConnection.make_request(self, action, path, headers, data, retry_handler=self._retry_handler)
def make_request(self, verb, resource, headers=None, data='', expected_status=None, params=None): if headers is None: headers = {} response = AWSAuthConnection.make_request( self, verb, resource, headers=headers, data=data, params=params) body = json.loads(response.read().decode('utf-8')) if response.status == expected_status: return body else: raise JSONResponseError(response.status, response.reason, body)
def make_request(self, method, bucket="", key="", headers=None, data="", query_args=None, sender=None): if isinstance(bucket, Bucket): bucket = bucket.name if isinstance(key, Key): key = key.name path = self.calling_format.build_path_base(bucket, key) auth_path = self.calling_format.build_auth_path(bucket, key) host = self.calling_format.build_host(self.server, bucket) if query_args: path += "?" + query_args auth_path += "?" + query_args return AWSAuthConnection.make_request(self, method, path, headers, data, host, auth_path, sender)
def make_request(self, verb, resource, headers=None, data='', expected_status=None, params=None): if headers is None: headers = {} response = AWSAuthConnection.make_request( self, verb, resource, headers=headers, data=data, params=params) body = json.loads(response.read().decode('utf-8')) if response.status == expected_status: return body else: error_type = response.getheader('x-amzn-ErrorType').split(':')[0] error_class = self._faults.get(error_type, self.ResponseError) raise error_class(response.status, response.reason, body)
def make_request(self, method, path, headers=None, data=''): # add auth header if headers == None: headers = {} if not headers.has_key('AWS-Version'): headers['AWS-Version'] = self.APIVersion if not headers.has_key('Content-Type'): headers['Content-Type'] = self.DefaultContentType return AWSAuthConnection.make_request(self, method, path, headers, data)
def make_request(self, method, path, headers=None, data=''): # add auth header if headers == None: headers = {} if not headers.has_key('AWS-Version'): headers['AWS-Version'] = self.APIVersion if not headers.has_key('Content-Type'): headers['Content-Type'] = self.DefaultContentType return AWSAuthConnection.make_request(self, method, path, headers, data)
def make_request(self, verb, resource, headers=None, data='', expected_status=None, params=None): if headers is None: headers = {} response = AWSAuthConnection.make_request( self, verb, resource, headers=headers, data=data) body = json.load(response) if response.status == expected_status: return body else: error_type = response.getheader('x-amzn-ErrorType').split(':')[0] error_class = self._faults.get(error_type, self.ResponseError) raise error_class(response.status, response.reason, body)
def make_request(self, method, bucket='', key='', headers=None, data='', query_args=None, sender=None): if isinstance(bucket, Bucket): bucket = bucket.name if isinstance(key, Key): key = key.name path = self.calling_format.build_path_base(bucket, key) auth_path = self.calling_format.build_auth_path(bucket, key) host = self.calling_format.build_host(self.server_name, bucket) if query_args: path += '?' + query_args auth_path += '?' + query_args return AWSAuthConnection.make_request(self, method, path, headers, data, host, auth_path, sender)
def get_user_quota(self, uid, tenant=''): """获取用户容量限制""" tenant = tenant or self.tenant if tenant: uid = '%s$%s' % (tenant, uid) parameters = {'uid': uid, 'quota-type': 'user'} response = AWSAuthConnection.make_request( self.conn, 'GET', self.admin_endpoint + 'user?quota&' + urllib.parse.urlencode(parameters), ) return self._handle_response(response)
def make_request(self, method, bucket='', key='', headers=None, data='', query_args=None, sender=None, override_num_retries=None): if isinstance(bucket, self.bucket_class): bucket = bucket.name if isinstance(key, Key): key = key.name path = self.calling_format.build_path_base(bucket, key) auth_path = self.calling_format.build_auth_path(bucket, key) host = self.calling_format.build_host(self.server_name(), bucket) if query_args: path += '?' + query_args auth_path += '?' + query_args return AWSAuthConnection.make_request(self, method, path, headers, data, host, auth_path, sender, override_num_retries=override_num_retries)
def make_request(self, method, bucket='', key='', extra_headers={}, data='', query_args=None, sender=None): if isinstance(bucket, Bucket): bucket = bucket.name if isinstance(key, Key): key = key.name path = self.calling_format.build_path_base(bucket, key) auth_path = self.calling_format.build_auth_path(bucket, key) host = self.calling_format.build_host(self.server_name(), bucket) if query_args: path += '?' + query_args auth_path += '?' + query_args headers = self.headers.copy() headers.update(extra_headers) return AWSAuthConnection.make_request(self, method, path, headers, data, host, auth_path, sender)
def make_request(self, verb, resource, headers=None, data='', ok_responses=(200,), params=None, response_headers=None): if headers is None: headers = {} headers['x-amz-glacier-version'] = self.Version uri = '/%s/%s' % (self.account_id, resource) response = AWSAuthConnection.make_request(self, verb, uri, params=params, headers=headers, data=data) if response.status in ok_responses: return GlacierResponse(response, response_headers) else: # create glacier-specific exceptions raise UnexpectedHTTPResponseError(ok_responses, response)
def link_bucket(self, uid, bucket, tenant=''): """将某个 bucket 绑定到指定 uid""" tenant = tenant or self.tenant if tenant: uid = '%s$%s' % (tenant, uid) parameters = { 'uid': uid, 'bucket': bucket, } response = AWSAuthConnection.make_request( self.conn, 'PUT', self.admin_endpoint + 'bucket?' + urllib.parse.urlencode(parameters), ) return self._handle_response(response)
def get_user_info(self, uid, tenant=None): """根据 uin 获取用户信息""" tenant = tenant or self.tenant if tenant: uid = '%s$%s' % (tenant, uid) parameters = {'uid': uid} response = AWSAuthConnection.make_request( self.conn, 'GET', self.admin_endpoint + 'user?' + urllib.parse.urlencode(parameters), ) body = response.read() if response.status == 200: return json.loads(body) elif response.status == 404: return None else: raise StorageResponseError(response.status, response.reason, body)
def make_request(conn, method, basepath='', resource='', headers=None, data=None, special_first_param=None, params=None, _retries=3): """ Returns a ``AWSAuthConnection.make_request`` call with the preserving of the special params done by ``build``. """ # request meta data md = build( conn, method, basepath=basepath, resource=resource, headers=headers, data=data, special_first_param=special_first_param, params=params, ) if params: # we basically need to do this ourselves now. BOTO doesn't do it for us # in make_request result = [] for k, vs in params.items(): if isinstance(vs, basestring) or not hasattr(vs, '__iter__'): vs = [vs] for v in vs: if v is not None: result.append( (k.encode('utf-8') if isinstance(k, str) else k, v.encode('utf-8') if isinstance(v, str) else v)) appending_char = '&' if md.special_first_param else '?' md.path = '%s%s%s' % (md.path, appending_char, urlencode(result, doseq=True)) return AWSAuthConnection.make_request( md.conn, md.method, md.path, headers=md.headers, data=md.data, host=md.host, auth_path=md.auth_path, params=md.params, override_num_retries=_retries )
def make_request( self, method, bucket="", key="", headers=None, data="", query_args=None, sender=None, override_num_retries=None ): if isinstance(bucket, self.bucket_class): bucket = bucket.name if isinstance(key, Key): key = key.name path = self.calling_format.build_path_base(bucket, key) boto.log.debug("path=%s" % path) auth_path = self.calling_format.build_auth_path(bucket, key) boto.log.debug("auth_path=%s" % auth_path) host = self.calling_format.build_host(self.server_name(), bucket) if query_args: path += "?" + query_args boto.log.debug("path=%s" % path) auth_path += "?" + query_args boto.log.debug("auth_path=%s" % auth_path) return AWSAuthConnection.make_request( self, method, path, headers, data, host, auth_path, sender, override_num_retries=override_num_retries )
def make_request(self, action, params=None, path='/', verb='GET'): """Overriden because we don't do the "Action" setting here""" headers = {} if params == None: params = {} params['Version'] = self.APIVersion params['AWSAccessKeyId'] = self.aws_access_key_id params['SignatureVersion'] = self.SignatureVersion params['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) qs, signature = self.get_signature(params, verb, self.get_path(path)) if verb == 'POST': headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8' request_body = qs + '&Signature=' + urllib.quote(signature) qs = path else: request_body = '' qs = path + '?' + qs + '&Signature=' + urllib.quote(signature) return AWSAuthConnection.make_request(self, verb, qs, data=request_body, headers=headers)
def make_request(self, verb, resource, headers=None, data='', expected_status=None, params=None): if headers is None: headers = {} response = AWSAuthConnection.make_request(self, verb, resource, headers=headers, data=data, params=params) body = json.loads(response.read().decode('utf-8')) if response.status == expected_status: return body else: raise JSONResponseError(response.status, response.reason, body)
def _query_server_state(self, conn, file_length): """ Queries server to find out state of given upload. Note that this method really just makes special case use of the fact that the upload server always returns the current start/end state whenever a PUT doesn't complete. Returns HTTP response from sending request. Raises ResumableUploadException if problem querying server. """ # Send an empty PUT so that server replies with this resumable # transfer's state. put_headers = {} put_headers['Content-Range'] = ( self._build_content_range_header('*', file_length)) put_headers['Content-Length'] = '0' return AWSAuthConnection.make_request(conn, 'PUT', path=self.tracker_uri_path, auth_path=self.tracker_uri_path, headers=put_headers, host=self.tracker_uri_host)
def make_request(self, action, params=None, path='/', verb='GET'): """Overriden because we don't do the "Action" setting here""" headers = {} if params == None: params = {} params['Version'] = self.APIVersion params['AWSAccessKeyId'] = self.aws_access_key_id params['SignatureVersion'] = self.SignatureVersion params['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) qs, signature = self.get_signature(params, verb, self.get_path(path)) if verb == 'POST': headers[ 'Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8' request_body = qs + '&Signature=' + urllib.quote(signature) qs = path else: request_body = '' qs = path + '?' + qs + '&Signature=' + urllib.quote(signature) return AWSAuthConnection.make_request(self, verb, qs, data=request_body, headers=headers)
def _make_request(self, action, params=None): """ Custom request method. SES uses a faux REST/RPC like interface. :type action: string :param action: API action :type params: Dict :param params: Additional params that are added to the API request. :type return: type returned from _process_response :param return: Formatted response """ params = params or {} params['Action'] = action headers = {'Content-Type': 'application/x-www-form-urlencoded'} path = '/%s' % self.Version pairs = [] for key, val in params.iteritems(): if val is None: continue pairs.append(key + '=' + urllib.quote(str(val))) data = '&' . join(pairs) response = AWSAuthConnection.make_request(self, 'POST', path, headers, data) return self._process_response(response)
def update_or_modify_user(self, method, uid, tenant=None, display_name='', email='', max_buckets=100): tenant = tenant or self.tenant if tenant: uid = '%s$%s' % (tenant, uid) parameters = { 'uid': uid, 'display-name': display_name, 'email': email, 'max-buckets': max_buckets } response = AWSAuthConnection.make_request( self.conn, method, self.admin_endpoint + 'user?' + urllib.parse.urlencode(parameters), # data=urllib.urlencode(parameters) ) return self._handle_response(response)
class TestMakeRequestRetriesWithCorrectHost(AWSMockServiceTestCase): def setUp(self): self.connection = AWSAuthConnection('s3.amazonaws.com') self.non_retriable_code = 404 self.retry_status_codes = [301, 400] self.success_response = self.create_response(200) self.default_host = 'bucket.s3.amazonaws.com' self.retry_region = RETRY_REGION_BYTES.decode('utf-8') self.default_retried_host = ('bucket.s3.%s.amazonaws.com' % self.retry_region) self.test_headers = [('x-amz-bucket-region', self.retry_region)] def test_non_retriable_status_returns_original_response(self): with mock.patch.object(self.connection, '_mexe') as mocked_mexe: error_response = self.create_response(self.non_retriable_code) mocked_mexe.side_effect = [error_response] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, error_response) # called_once_with does not compare equality correctly with # HTTPResponse objects. self.assertEqual(mocked_mexe.call_count, 1) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_host) def test_non_retriable_host_returns_original_response(self): for code in self.retry_status_codes: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: error_response = self.create_response(code) mocked_mexe.side_effect = [error_response] other_host = 'bucket.some-other-provider.com' response = self.connection.make_request('HEAD', '/', host=other_host) self.assertEqual(response, error_response) self.assertEqual(mocked_mexe.call_count, 1) self.assertEqual(mocked_mexe.call_args[0][0].host, other_host) def test_non_retriable_status_raises_original_exception(self): with mock.patch.object(self.connection, '_mexe') as mocked_mexe: error_response = S3ResponseError(self.non_retriable_code, 'reason') mocked_mexe.side_effect = [error_response] with self.assertRaises(S3ResponseError) as cm: self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(cm.exception, error_response) self.assertEqual(mocked_mexe.call_count, 1) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_host) def test_non_retriable_host_raises_original_exception(self): with mock.patch.object(self.connection, '_mexe') as mocked_mexe: error_response = S3ResponseError(self.non_retriable_code, 'reason') mocked_mexe.side_effect = [error_response] other_host = 'bucket.some-other-provider.com' with self.assertRaises(S3ResponseError) as cm: self.connection.make_request('HEAD', '/', host=other_host) self.assertEqual(cm.exception, error_response) self.assertEqual(mocked_mexe.call_count, 1) self.assertEqual(mocked_mexe.call_args[0][0].host, other_host) def test_response_retries_from_callable_headers(self): for code in self.retry_status_codes: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: mocked_mexe.side_effect = [ self.create_response(code, header=self.test_headers), self.success_response ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_retry_changes_host_with_region(self): with mock.patch.object(self.connection, '_mexe') as mocked_mexe: # Assume 400 with callable headers results uses the same url # manipulation as all of the other successful cases. mocked_mexe.side_effect = [ self.create_response(400, header=self.test_headers), self.success_response ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_retry_changes_host_with_multiple_s3_occurrences(self): with mock.patch.object(self.connection, '_mexe') as mocked_mexe: # Assume 400 with callable headers results uses the same url # manipulation as all of the other successful cases. mocked_mexe.side_effect = [ self.create_response(400, header=self.test_headers), self.success_response ] response = self.connection.make_request( 'HEAD', '/', host='a.s3.a.s3.amazonaws.com') self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, 'a.s3.a.s3.us-east-2.amazonaws.com') def test_retry_changes_host_with_s3_in_region(self): with mock.patch.object(self.connection, '_mexe') as mocked_mexe: # Assume 400 with callable headers results uses the same url # manipulation as all of the other successful cases. mocked_mexe.side_effect = [ self.create_response(400, header=self.test_headers), self.success_response ] response = self.connection.make_request( 'HEAD', '/', host='bucket.s3.asdf-s3.amazonaws.com') self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_response_body_parsed_for_region(self): for code, body in ERRORS_WITH_REGION_IN_BODY: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: mocked_mexe.side_effect = [ self.create_response(code, body=body), self.success_response ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_error_body_parsed_for_region(self): for code, body in ERRORS_WITH_REGION_IN_BODY: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: mocked_mexe.side_effect = [ S3ResponseError(code, 'reason', body=body), self.success_response ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_response_without_region_header_retries_from_bucket_head(self): for code in self.retry_status_codes: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: mocked_mexe.side_effect = [ self.create_response(code), self.create_response(200, header=self.test_headers), self.success_response ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 3) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_response_body_without_region_sends_bucket_head(self): for code, body in ERRORS_WITHOUT_REGION_IN_BODY: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: mocked_mexe.side_effect = [ self.create_response(code, body=body), self.create_response(200, header=self.test_headers), self.success_response ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 3) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_error_body_without_region_retries_from_bucket_head_request(self): for code, body in ERRORS_WITHOUT_REGION_IN_BODY: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: mocked_mexe.side_effect = [ S3ResponseError(code, 'reason', body=body), self.create_response(200, header=self.test_headers), self.success_response ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, self.success_response) self.assertEqual(mocked_mexe.call_count, 3) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_retried_host) def test_retry_head_request_lacks_region_returns_original_response(self): for code in self.retry_status_codes: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: error_response = self.create_response(code) mocked_mexe.side_effect = [ error_response, self.create_response(200, header=[]) # no region in header. ] response = self.connection.make_request('HEAD', '/', host=self.default_host) self.assertEqual(response, error_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_host) def test_retry_head_request_lacks_region_raises_original_exception(self): for code in self.retry_status_codes: with mock.patch.object(self.connection, '_mexe') as mocked_mexe: error_response = S3ResponseError(code, 'reason') mocked_mexe.side_effect = [ error_response, self.create_response(200, header=[]) ] with self.assertRaises(S3ResponseError) as cm: response = self.connection.make_request( 'HEAD', '/', host=self.default_host) self.assertEqual(cm.exception, error_response) self.assertEqual(mocked_mexe.call_count, 2) self.assertEqual(mocked_mexe.call_args[0][0].host, self.default_host)
def make_request(self, params): data = '&'.join(k + '=' + urllib.quote(v) for k, v in params.iteritems()) return AWSAuthConnection.make_request(self, 'GET', '/?' + data)