| Index: third_party/boto/boto/s3/connection.py
|
| ===================================================================
|
| --- third_party/boto/boto/s3/connection.py (revision 33376)
|
| +++ third_party/boto/boto/s3/connection.py (working copy)
|
| @@ -148,6 +148,16 @@
|
| CNNorth1 = 'cn-north-1'
|
|
|
|
|
| +class NoHostProvided(object):
|
| + # An identifying object to help determine whether the user provided a
|
| + # ``host`` or not. Never instantiated.
|
| + pass
|
| +
|
| +
|
| +class HostRequiredError(BotoClientError):
|
| + pass
|
| +
|
| +
|
| class S3Connection(AWSAuthConnection):
|
|
|
| DefaultHost = boto.config.get('s3', 'host', 's3.amazonaws.com')
|
| @@ -157,11 +167,15 @@
|
| def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
|
| is_secure=True, port=None, proxy=None, proxy_port=None,
|
| proxy_user=None, proxy_pass=None,
|
| - host=DefaultHost, debug=0, https_connection_factory=None,
|
| + host=NoHostProvided, debug=0, https_connection_factory=None,
|
| calling_format=DefaultCallingFormat, path='/',
|
| provider='aws', bucket_class=Bucket, security_token=None,
|
| suppress_consec_slashes=True, anon=False,
|
| - validate_certs=None):
|
| + validate_certs=None, profile_name=None):
|
| + no_host_provided = False
|
| + if host is NoHostProvided:
|
| + no_host_provided = True
|
| + host = self.DefaultHost
|
| if isinstance(calling_format, basestring):
|
| calling_format=boto.utils.find_class(calling_format)()
|
| self.calling_format = calling_format
|
| @@ -173,7 +187,14 @@
|
| debug=debug, https_connection_factory=https_connection_factory,
|
| path=path, provider=provider, security_token=security_token,
|
| suppress_consec_slashes=suppress_consec_slashes,
|
| - validate_certs=validate_certs)
|
| + validate_certs=validate_certs, profile_name=profile_name)
|
| + # We need to delay until after the call to ``super`` before checking
|
| + # to see if SigV4 is in use.
|
| + if no_host_provided:
|
| + if 'hmac-v4-s3' in self._required_auth_capability():
|
| + raise HostRequiredError(
|
| + "When using SigV4, you must specify a 'host' parameter."
|
| + )
|
|
|
| @detect_potential_s3sigv4
|
| def _required_auth_capability(self):
|
| @@ -329,9 +350,34 @@
|
|
|
| return {"action": url, "fields": fields}
|
|
|
| + def generate_url_sigv4(self, expires_in, method, bucket='', key='',
|
| + headers=None, force_http=False,
|
| + response_headers=None, version_id=None,
|
| + iso_date=None):
|
| + 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)
|
| +
|
| + params = {}
|
| + if version_id is not None:
|
| + params['VersionId'] = version_id
|
| +
|
| + http_request = self.build_base_http_request(method, path, auth_path,
|
| + headers=headers, host=host,
|
| + params=params)
|
| +
|
| + return self._auth_handler.presign(http_request, expires_in,
|
| + iso_date=iso_date)
|
| +
|
| def generate_url(self, expires_in, method, bucket='', key='', headers=None,
|
| query_auth=True, force_http=False, response_headers=None,
|
| expires_in_absolute=False, version_id=None):
|
| + if self._auth_handler.capability[0] == 'hmac-v4-s3':
|
| + # Handle the special sigv4 case
|
| + return self.generate_url_sigv4(expires_in, method, bucket=bucket,
|
| + key=key, headers=headers, force_http=force_http,
|
| + response_headers=response_headers, version_id=version_id)
|
| +
|
| headers = headers or {}
|
| if expires_in_absolute:
|
| expires = int(expires_in)
|
| @@ -418,6 +464,23 @@
|
| ``S3Connection.lookup`` method, which will either return a valid bucket
|
| or ``None``.
|
|
|
| + If ``validate=False`` is passed, no request is made to the service (no
|
| + charge/communication delay). This is only safe to do if you are **sure**
|
| + the bucket exists.
|
| +
|
| + If the default ``validate=True`` is passed, a request is made to the
|
| + service to ensure the bucket exists. Prior to Boto v2.25.0, this fetched
|
| + a list of keys (but with a max limit set to ``0``, always returning an empty
|
| + list) in the bucket (& included better error messages), at an
|
| + increased expense. As of Boto v2.25.0, this now performs a HEAD request
|
| + (less expensive but worse error messages).
|
| +
|
| + If you were relying on parsing the error message before, you should call
|
| + something like::
|
| +
|
| + bucket = conn.get_bucket('<bucket_name>', validate=False)
|
| + bucket.get_all_keys(maxkeys=0)
|
| +
|
| :type bucket_name: string
|
| :param bucket_name: The name of the bucket
|
|
|
| @@ -426,14 +489,59 @@
|
| AWS.
|
|
|
| :type validate: boolean
|
| - :param validate: If ``True``, it will try to fetch all keys within the
|
| - given bucket. (Default: ``True``)
|
| + :param validate: If ``True``, it will try to verify the bucket exists
|
| + on the service-side. (Default: ``True``)
|
| """
|
| - bucket = self.bucket_class(self, bucket_name)
|
| if validate:
|
| - bucket.get_all_keys(headers, maxkeys=0)
|
| - return bucket
|
| + return self.head_bucket(bucket_name, headers=headers)
|
| + else:
|
| + return self.bucket_class(self, bucket_name)
|
|
|
| + def head_bucket(self, bucket_name, headers=None):
|
| + """
|
| + Determines if a bucket exists by name.
|
| +
|
| + If the bucket does not exist, an ``S3ResponseError`` will be raised.
|
| +
|
| + :type bucket_name: string
|
| + :param bucket_name: The name of the bucket
|
| +
|
| + :type headers: dict
|
| + :param headers: Additional headers to pass along with the request to
|
| + AWS.
|
| +
|
| + :returns: A <Bucket> object
|
| + """
|
| + response = self.make_request('HEAD', bucket_name, headers=headers)
|
| + body = response.read()
|
| + if response.status == 200:
|
| + return self.bucket_class(self, bucket_name)
|
| + elif response.status == 403:
|
| + # For backward-compatibility, we'll populate part of the exception
|
| + # with the most-common default.
|
| + err = self.provider.storage_response_error(
|
| + response.status,
|
| + response.reason,
|
| + body
|
| + )
|
| + err.error_code = 'AccessDenied'
|
| + err.error_message = 'Access Denied'
|
| + raise err
|
| + elif response.status == 404:
|
| + # For backward-compatibility, we'll populate part of the exception
|
| + # with the most-common default.
|
| + err = self.provider.storage_response_error(
|
| + response.status,
|
| + response.reason,
|
| + body
|
| + )
|
| + err.error_code = 'NoSuchBucket'
|
| + err.error_message = 'The specified bucket does not exist'
|
| + raise err
|
| + else:
|
| + raise self.provider.storage_response_error(
|
| + response.status, response.reason, body)
|
| +
|
| def lookup(self, bucket_name, validate=True, headers=None):
|
| """
|
| Attempts to get a bucket from S3.
|
|
|