| Index: boto/s3/connection.py
|
| diff --git a/boto/s3/connection.py b/boto/s3/connection.py
|
| index 25ba4aba5a763de2784eabd2dd051308672d4a92..80209b7dbc5abbedd4170ee958201c9a808754f0 100644
|
| --- a/boto/s3/connection.py
|
| +++ b/boto/s3/connection.py
|
| @@ -27,7 +27,6 @@ import time
|
| import boto.utils
|
| from boto.connection import AWSAuthConnection
|
| from boto import handler
|
| -from boto.provider import Provider
|
| from boto.s3.bucket import Bucket
|
| from boto.s3.key import Key
|
| from boto.resultset import ResultSet
|
| @@ -64,7 +63,10 @@ def assert_case_insensitive(f):
|
| return f(*args, **kwargs)
|
| return wrapper
|
|
|
| -class _CallingFormat:
|
| +class _CallingFormat(object):
|
| +
|
| + def get_bucket_server(self, server, bucket):
|
| + return ''
|
|
|
| def build_url_base(self, connection, protocol, server, bucket, key=''):
|
| url_base = '%s://' % protocol
|
| @@ -79,12 +81,14 @@ class _CallingFormat:
|
| return self.get_bucket_server(server, bucket)
|
|
|
| def build_auth_path(self, bucket, key=''):
|
| + key = boto.utils.get_utf8_value(key)
|
| path = ''
|
| if bucket != '':
|
| path = '/' + bucket
|
| return path + '/%s' % urllib.quote(key)
|
|
|
| def build_path_base(self, bucket, key=''):
|
| + key = boto.utils.get_utf8_value(key)
|
| return '/%s' % urllib.quote(key)
|
|
|
| class SubdomainCallingFormat(_CallingFormat):
|
| @@ -105,15 +109,25 @@ class OrdinaryCallingFormat(_CallingFormat):
|
| return server
|
|
|
| def build_path_base(self, bucket, key=''):
|
| + key = boto.utils.get_utf8_value(key)
|
| path_base = '/'
|
| if bucket:
|
| path_base += "%s/" % bucket
|
| return path_base + urllib.quote(key)
|
|
|
| +class ProtocolIndependentOrdinaryCallingFormat(OrdinaryCallingFormat):
|
| +
|
| + def build_url_base(self, connection, protocol, server, bucket, key=''):
|
| + url_base = '//'
|
| + url_base += self.build_host(server, bucket)
|
| + url_base += connection.get_path(self.build_path_base(bucket, key))
|
| + return url_base
|
| +
|
| class Location:
|
| DEFAULT = '' # US Classic Region
|
| EU = 'EU'
|
| USWest = 'us-west-1'
|
| + APNortheast = 'ap-northeast-1'
|
| APSoutheast = 'ap-southeast-1'
|
|
|
| class S3Connection(AWSAuthConnection):
|
| @@ -125,15 +139,15 @@ class S3Connection(AWSAuthConnection):
|
| is_secure=True, port=None, proxy=None, proxy_port=None,
|
| proxy_user=None, proxy_pass=None,
|
| host=DefaultHost, debug=0, https_connection_factory=None,
|
| - calling_format=SubdomainCallingFormat(), path='/', provider='aws',
|
| - bucket_class=Bucket):
|
| + calling_format=SubdomainCallingFormat(), path='/',
|
| + provider='aws', bucket_class=Bucket, security_token=None):
|
| self.calling_format = calling_format
|
| self.bucket_class = bucket_class
|
| AWSAuthConnection.__init__(self, host,
|
| aws_access_key_id, aws_secret_access_key,
|
| is_secure, port, proxy, proxy_port, proxy_user, proxy_pass,
|
| debug=debug, https_connection_factory=https_connection_factory,
|
| - path=path, provider=provider)
|
| + path=path, provider=provider, security_token=security_token)
|
|
|
| def _required_auth_capability(self):
|
| return ['s3']
|
| @@ -143,7 +157,7 @@ class S3Connection(AWSAuthConnection):
|
| yield bucket
|
|
|
| def __contains__(self, bucket_name):
|
| - return not (self.lookup(bucket_name) is None)
|
| + return not (self.lookup(bucket_name) is None)
|
|
|
| def set_bucket_class(self, bucket_class):
|
| """
|
| @@ -170,8 +184,10 @@ class S3Connection(AWSAuthConnection):
|
|
|
|
|
| def build_post_form_args(self, bucket_name, key, expires_in = 6000,
|
| - acl = None, success_action_redirect = None, max_content_length = None,
|
| - http_method = "http", fields=None, conditions=None):
|
| + acl = None, success_action_redirect = None,
|
| + max_content_length = None,
|
| + http_method = "http", fields=None,
|
| + conditions=None):
|
| """
|
| Taken from the AWS book Python examples and modified for use with boto
|
| This only returns the arguments required for the post form, not the actual form
|
| @@ -261,13 +277,22 @@ class S3Connection(AWSAuthConnection):
|
| return {"action": url, "fields": fields}
|
|
|
|
|
| - def generate_url(self, expires_in, method, bucket='', key='',
|
| - headers=None, query_auth=True, force_http=False):
|
| + def generate_url(self, expires_in, method, bucket='', key='', headers=None,
|
| + query_auth=True, force_http=False, response_headers=None):
|
| if not headers:
|
| headers = {}
|
| expires = int(time.time() + expires_in)
|
| auth_path = self.calling_format.build_auth_path(bucket, key)
|
| auth_path = self.get_path(auth_path)
|
| + # Arguments to override response headers become part of the canonical
|
| + # string to be signed.
|
| + if response_headers:
|
| + response_hdrs = ["%s=%s" % (k, v) for k, v in
|
| + response_headers.items()]
|
| + delimiter = '?' if '?' not in auth_path else '&'
|
| + auth_path = "%s%s%s" % (auth_path, delimiter, '&'.join(response_hdrs))
|
| + else:
|
| + response_headers = {}
|
| c_string = boto.utils.canonical_string(method, auth_path, headers,
|
| expires, self.provider)
|
| b64_hmac = self._auth_handler.sign_string(c_string)
|
| @@ -275,11 +300,13 @@ class S3Connection(AWSAuthConnection):
|
| self.calling_format.build_path_base(bucket, key)
|
| if query_auth:
|
| query_part = '?' + self.QueryString % (encoded_canonical, expires,
|
| - self.aws_access_key_id)
|
| - sec_hdr = self.provider.security_token_header
|
| - if sec_hdr in headers:
|
| - query_part += ('&%s=%s' % (sec_hdr,
|
| - urllib.quote(headers[sec_hdr])));
|
| + self.aws_access_key_id)
|
| + # The response headers must also be GET parameters in the URL.
|
| + headers.update(response_headers)
|
| + hdrs = [ '%s=%s'%(name, urllib.quote(val)) for name,val in headers.items() ]
|
| + q_str = '&'.join(hdrs)
|
| + if q_str:
|
| + query_part += '&' + q_str
|
| else:
|
| query_part = ''
|
| if force_http:
|
| @@ -304,11 +331,13 @@ class S3Connection(AWSAuthConnection):
|
|
|
| def get_canonical_user_id(self, headers=None):
|
| """
|
| - Convenience method that returns the "CanonicalUserID" of the user who's credentials
|
| - are associated with the connection. The only way to get this value is to do a GET
|
| - request on the service which returns all buckets associated with the account. As part
|
| - of that response, the canonical userid is returned. This method simply does all of
|
| - that and then returns just the user id.
|
| + Convenience method that returns the "CanonicalUserID" of the
|
| + user who's credentials are associated with the connection.
|
| + The only way to get this value is to do a GET request on the
|
| + service which returns all buckets associated with the account.
|
| + As part of that response, the canonical userid is returned.
|
| + This method simply does all of that and then returns just the
|
| + user id.
|
|
|
| :rtype: string
|
| :return: A string containing the canonical user id.
|
|
|