Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(406)

Side by Side Diff: boto/s3/connection.py

Issue 8386013: Merging in latest boto. (Closed) Base URL: svn://svn.chromium.org/boto
Patch Set: Redoing vendor drop by deleting and then merging. Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « boto/s3/bucketlistresultset.py ('k') | boto/s3/key.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ 1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/
2 # Copyright (c) 2010, Eucalyptus Systems, Inc. 2 # Copyright (c) 2010, Eucalyptus Systems, Inc.
3 # All rights reserved. 3 # All rights reserved.
4 # 4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a 5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the 6 # copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including 7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish, dis- 8 # without limitation the rights to use, copy, modify, merge, publish, dis-
9 # tribute, sublicense, and/or sell copies of the Software, and to permit 9 # tribute, sublicense, and/or sell copies of the Software, and to permit
10 # persons to whom the Software is furnished to do so, subject to the fol- 10 # persons to whom the Software is furnished to do so, subject to the fol-
11 # lowing conditions: 11 # lowing conditions:
12 # 12 #
13 # The above copyright notice and this permission notice shall be included 13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software. 14 # in all copies or substantial portions of the Software.
15 # 15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
18 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 18 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 # IN THE SOFTWARE. 22 # IN THE SOFTWARE.
23 23
24 import xml.sax 24 import xml.sax
25 import urllib, base64 25 import urllib, base64
26 import time 26 import time
27 import boto.utils 27 import boto.utils
28 from boto.connection import AWSAuthConnection 28 from boto.connection import AWSAuthConnection
29 from boto import handler 29 from boto import handler
30 from boto.provider import Provider
31 from boto.s3.bucket import Bucket 30 from boto.s3.bucket import Bucket
32 from boto.s3.key import Key 31 from boto.s3.key import Key
33 from boto.resultset import ResultSet 32 from boto.resultset import ResultSet
34 from boto.exception import BotoClientError 33 from boto.exception import BotoClientError
35 34
36 def check_lowercase_bucketname(n): 35 def check_lowercase_bucketname(n):
37 """ 36 """
38 Bucket names must not contain uppercase characters. We check for 37 Bucket names must not contain uppercase characters. We check for
39 this by appending a lowercase character and testing with islower(). 38 this by appending a lowercase character and testing with islower().
40 Note this also covers cases like numeric bucket names with dashes. 39 Note this also covers cases like numeric bucket names with dashes.
(...skipping 16 matching lines...) Expand all
57 "hosting calling format.") 56 "hosting calling format.")
58 return True 57 return True
59 58
60 def assert_case_insensitive(f): 59 def assert_case_insensitive(f):
61 def wrapper(*args, **kwargs): 60 def wrapper(*args, **kwargs):
62 if len(args) == 3 and check_lowercase_bucketname(args[2]): 61 if len(args) == 3 and check_lowercase_bucketname(args[2]):
63 pass 62 pass
64 return f(*args, **kwargs) 63 return f(*args, **kwargs)
65 return wrapper 64 return wrapper
66 65
67 class _CallingFormat: 66 class _CallingFormat(object):
67
68 def get_bucket_server(self, server, bucket):
69 return ''
68 70
69 def build_url_base(self, connection, protocol, server, bucket, key=''): 71 def build_url_base(self, connection, protocol, server, bucket, key=''):
70 url_base = '%s://' % protocol 72 url_base = '%s://' % protocol
71 url_base += self.build_host(server, bucket) 73 url_base += self.build_host(server, bucket)
72 url_base += connection.get_path(self.build_path_base(bucket, key)) 74 url_base += connection.get_path(self.build_path_base(bucket, key))
73 return url_base 75 return url_base
74 76
75 def build_host(self, server, bucket): 77 def build_host(self, server, bucket):
76 if bucket == '': 78 if bucket == '':
77 return server 79 return server
78 else: 80 else:
79 return self.get_bucket_server(server, bucket) 81 return self.get_bucket_server(server, bucket)
80 82
81 def build_auth_path(self, bucket, key=''): 83 def build_auth_path(self, bucket, key=''):
84 key = boto.utils.get_utf8_value(key)
82 path = '' 85 path = ''
83 if bucket != '': 86 if bucket != '':
84 path = '/' + bucket 87 path = '/' + bucket
85 return path + '/%s' % urllib.quote(key) 88 return path + '/%s' % urllib.quote(key)
86 89
87 def build_path_base(self, bucket, key=''): 90 def build_path_base(self, bucket, key=''):
91 key = boto.utils.get_utf8_value(key)
88 return '/%s' % urllib.quote(key) 92 return '/%s' % urllib.quote(key)
89 93
90 class SubdomainCallingFormat(_CallingFormat): 94 class SubdomainCallingFormat(_CallingFormat):
91 95
92 @assert_case_insensitive 96 @assert_case_insensitive
93 def get_bucket_server(self, server, bucket): 97 def get_bucket_server(self, server, bucket):
94 return '%s.%s' % (bucket, server) 98 return '%s.%s' % (bucket, server)
95 99
96 class VHostCallingFormat(_CallingFormat): 100 class VHostCallingFormat(_CallingFormat):
97 101
98 @assert_case_insensitive 102 @assert_case_insensitive
99 def get_bucket_server(self, server, bucket): 103 def get_bucket_server(self, server, bucket):
100 return bucket 104 return bucket
101 105
102 class OrdinaryCallingFormat(_CallingFormat): 106 class OrdinaryCallingFormat(_CallingFormat):
103 107
104 def get_bucket_server(self, server, bucket): 108 def get_bucket_server(self, server, bucket):
105 return server 109 return server
106 110
107 def build_path_base(self, bucket, key=''): 111 def build_path_base(self, bucket, key=''):
112 key = boto.utils.get_utf8_value(key)
108 path_base = '/' 113 path_base = '/'
109 if bucket: 114 if bucket:
110 path_base += "%s/" % bucket 115 path_base += "%s/" % bucket
111 return path_base + urllib.quote(key) 116 return path_base + urllib.quote(key)
112 117
118 class ProtocolIndependentOrdinaryCallingFormat(OrdinaryCallingFormat):
119
120 def build_url_base(self, connection, protocol, server, bucket, key=''):
121 url_base = '//'
122 url_base += self.build_host(server, bucket)
123 url_base += connection.get_path(self.build_path_base(bucket, key))
124 return url_base
125
113 class Location: 126 class Location:
114 DEFAULT = '' # US Classic Region 127 DEFAULT = '' # US Classic Region
115 EU = 'EU' 128 EU = 'EU'
116 USWest = 'us-west-1' 129 USWest = 'us-west-1'
130 APNortheast = 'ap-northeast-1'
117 APSoutheast = 'ap-southeast-1' 131 APSoutheast = 'ap-southeast-1'
118 132
119 class S3Connection(AWSAuthConnection): 133 class S3Connection(AWSAuthConnection):
120 134
121 DefaultHost = 's3.amazonaws.com' 135 DefaultHost = 's3.amazonaws.com'
122 QueryString = 'Signature=%s&Expires=%d&AWSAccessKeyId=%s' 136 QueryString = 'Signature=%s&Expires=%d&AWSAccessKeyId=%s'
123 137
124 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, 138 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
125 is_secure=True, port=None, proxy=None, proxy_port=None, 139 is_secure=True, port=None, proxy=None, proxy_port=None,
126 proxy_user=None, proxy_pass=None, 140 proxy_user=None, proxy_pass=None,
127 host=DefaultHost, debug=0, https_connection_factory=None, 141 host=DefaultHost, debug=0, https_connection_factory=None,
128 calling_format=SubdomainCallingFormat(), path='/', provider='aw s', 142 calling_format=SubdomainCallingFormat(), path='/',
129 bucket_class=Bucket): 143 provider='aws', bucket_class=Bucket, security_token=None):
130 self.calling_format = calling_format 144 self.calling_format = calling_format
131 self.bucket_class = bucket_class 145 self.bucket_class = bucket_class
132 AWSAuthConnection.__init__(self, host, 146 AWSAuthConnection.__init__(self, host,
133 aws_access_key_id, aws_secret_access_key, 147 aws_access_key_id, aws_secret_access_key,
134 is_secure, port, proxy, proxy_port, proxy_user, proxy_pass, 148 is_secure, port, proxy, proxy_port, proxy_user, proxy_pass,
135 debug=debug, https_connection_factory=https_connection_factory, 149 debug=debug, https_connection_factory=https_connection_factory,
136 path=path, provider=provider) 150 path=path, provider=provider, security_token=security_token)
137 151
138 def _required_auth_capability(self): 152 def _required_auth_capability(self):
139 return ['s3'] 153 return ['s3']
140 154
141 def __iter__(self): 155 def __iter__(self):
142 for bucket in self.get_all_buckets(): 156 for bucket in self.get_all_buckets():
143 yield bucket 157 yield bucket
144 158
145 def __contains__(self, bucket_name): 159 def __contains__(self, bucket_name):
146 return not (self.lookup(bucket_name) is None) 160 return not (self.lookup(bucket_name) is None)
147 161
148 def set_bucket_class(self, bucket_class): 162 def set_bucket_class(self, bucket_class):
149 """ 163 """
150 Set the Bucket class associated with this bucket. By default, this 164 Set the Bucket class associated with this bucket. By default, this
151 would be the boto.s3.key.Bucket class but if you want to subclass that 165 would be the boto.s3.key.Bucket class but if you want to subclass that
152 for some reason this allows you to associate your new class. 166 for some reason this allows you to associate your new class.
153 167
154 :type bucket_class: class 168 :type bucket_class: class
155 :param bucket_class: A subclass of Bucket that can be more specific 169 :param bucket_class: A subclass of Bucket that can be more specific
156 """ 170 """
157 self.bucket_class = bucket_class 171 self.bucket_class = bucket_class
158 172
159 def build_post_policy(self, expiration_time, conditions): 173 def build_post_policy(self, expiration_time, conditions):
160 """ 174 """
161 Taken from the AWS book Python examples and modified for use with boto 175 Taken from the AWS book Python examples and modified for use with boto
162 """ 176 """
163 assert type(expiration_time) == time.struct_time, \ 177 assert type(expiration_time) == time.struct_time, \
164 'Policy document must include a valid expiration Time object' 178 'Policy document must include a valid expiration Time object'
165 179
166 # Convert conditions object mappings to condition statements 180 # Convert conditions object mappings to condition statements
167 181
168 return '{"expiration": "%s",\n"conditions": [%s]}' % \ 182 return '{"expiration": "%s",\n"conditions": [%s]}' % \
169 (time.strftime(boto.utils.ISO8601, expiration_time), ",".join(condit ions)) 183 (time.strftime(boto.utils.ISO8601, expiration_time), ",".join(condit ions))
170 184
171 185
172 def build_post_form_args(self, bucket_name, key, expires_in = 6000, 186 def build_post_form_args(self, bucket_name, key, expires_in = 6000,
173 acl = None, success_action_redirect = None, max_content_ length = None, 187 acl = None, success_action_redirect = None,
174 http_method = "http", fields=None, conditions=None): 188 max_content_length = None,
189 http_method = "http", fields=None,
190 conditions=None):
175 """ 191 """
176 Taken from the AWS book Python examples and modified for use with boto 192 Taken from the AWS book Python examples and modified for use with boto
177 This only returns the arguments required for the post form, not the actu al form 193 This only returns the arguments required for the post form, not the actu al form
178 This does not return the file input field which also needs to be added 194 This does not return the file input field which also needs to be added
179 195
180 :param bucket_name: Bucket to submit to 196 :param bucket_name: Bucket to submit to
181 :type bucket_name: string 197 :type bucket_name: string
182 198
183 :param key: Key name, optionally add ${filename} to the end to attach t he submitted filename 199 :param key: Key name, optionally add ${filename} to the end to attach t he submitted filename
184 :type key: string 200 :type key: string
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 signature = self._auth_handler.sign_string(policy_b64) 270 signature = self._auth_handler.sign_string(policy_b64)
255 fields.append({"name": "signature", "value": signature}) 271 fields.append({"name": "signature", "value": signature})
256 fields.append({"name": "key", "value": key}) 272 fields.append({"name": "key", "value": key})
257 273
258 # HTTPS protocol will be used if the secure HTTP option is enabled. 274 # HTTPS protocol will be used if the secure HTTP option is enabled.
259 url = '%s://%s/' % (http_method, self.calling_format.build_host(self.ser ver_name(), bucket_name)) 275 url = '%s://%s/' % (http_method, self.calling_format.build_host(self.ser ver_name(), bucket_name))
260 276
261 return {"action": url, "fields": fields} 277 return {"action": url, "fields": fields}
262 278
263 279
264 def generate_url(self, expires_in, method, bucket='', key='', 280 def generate_url(self, expires_in, method, bucket='', key='', headers=None,
265 headers=None, query_auth=True, force_http=False): 281 query_auth=True, force_http=False, response_headers=None):
266 if not headers: 282 if not headers:
267 headers = {} 283 headers = {}
268 expires = int(time.time() + expires_in) 284 expires = int(time.time() + expires_in)
269 auth_path = self.calling_format.build_auth_path(bucket, key) 285 auth_path = self.calling_format.build_auth_path(bucket, key)
270 auth_path = self.get_path(auth_path) 286 auth_path = self.get_path(auth_path)
287 # Arguments to override response headers become part of the canonical
288 # string to be signed.
289 if response_headers:
290 response_hdrs = ["%s=%s" % (k, v) for k, v in
291 response_headers.items()]
292 delimiter = '?' if '?' not in auth_path else '&'
293 auth_path = "%s%s%s" % (auth_path, delimiter, '&'.join(response_hdrs ))
294 else:
295 response_headers = {}
271 c_string = boto.utils.canonical_string(method, auth_path, headers, 296 c_string = boto.utils.canonical_string(method, auth_path, headers,
272 expires, self.provider) 297 expires, self.provider)
273 b64_hmac = self._auth_handler.sign_string(c_string) 298 b64_hmac = self._auth_handler.sign_string(c_string)
274 encoded_canonical = urllib.quote_plus(b64_hmac) 299 encoded_canonical = urllib.quote_plus(b64_hmac)
275 self.calling_format.build_path_base(bucket, key) 300 self.calling_format.build_path_base(bucket, key)
276 if query_auth: 301 if query_auth:
277 query_part = '?' + self.QueryString % (encoded_canonical, expires, 302 query_part = '?' + self.QueryString % (encoded_canonical, expires,
278 self.aws_access_key_id) 303 self.aws_access_key_id)
279 sec_hdr = self.provider.security_token_header 304 # The response headers must also be GET parameters in the URL.
280 if sec_hdr in headers: 305 headers.update(response_headers)
281 query_part += ('&%s=%s' % (sec_hdr, 306 hdrs = [ '%s=%s'%(name, urllib.quote(val)) for name,val in headers.i tems() ]
282 urllib.quote(headers[sec_hdr]))); 307 q_str = '&'.join(hdrs)
308 if q_str:
309 query_part += '&' + q_str
283 else: 310 else:
284 query_part = '' 311 query_part = ''
285 if force_http: 312 if force_http:
286 protocol = 'http' 313 protocol = 'http'
287 port = 80 314 port = 80
288 else: 315 else:
289 protocol = self.protocol 316 protocol = self.protocol
290 port = self.port 317 port = self.port
291 return self.calling_format.build_url_base(self, protocol, self.server_na me(port), 318 return self.calling_format.build_url_base(self, protocol, self.server_na me(port),
292 bucket, key) + query_part 319 bucket, key) + query_part
293 320
294 def get_all_buckets(self, headers=None): 321 def get_all_buckets(self, headers=None):
295 response = self.make_request('GET', headers=headers) 322 response = self.make_request('GET', headers=headers)
296 body = response.read() 323 body = response.read()
297 if response.status > 300: 324 if response.status > 300:
298 raise self.provider.storage_response_error( 325 raise self.provider.storage_response_error(
299 response.status, response.reason, body) 326 response.status, response.reason, body)
300 rs = ResultSet([('Bucket', self.bucket_class)]) 327 rs = ResultSet([('Bucket', self.bucket_class)])
301 h = handler.XmlHandler(rs, self) 328 h = handler.XmlHandler(rs, self)
302 xml.sax.parseString(body, h) 329 xml.sax.parseString(body, h)
303 return rs 330 return rs
304 331
305 def get_canonical_user_id(self, headers=None): 332 def get_canonical_user_id(self, headers=None):
306 """ 333 """
307 Convenience method that returns the "CanonicalUserID" of the user who's credentials 334 Convenience method that returns the "CanonicalUserID" of the
308 are associated with the connection. The only way to get this value is t o do a GET 335 user who's credentials are associated with the connection.
309 request on the service which returns all buckets associated with the acc ount. As part 336 The only way to get this value is to do a GET request on the
310 of that response, the canonical userid is returned. This method simply does all of 337 service which returns all buckets associated with the account.
311 that and then returns just the user id. 338 As part of that response, the canonical userid is returned.
339 This method simply does all of that and then returns just the
340 user id.
312 341
313 :rtype: string 342 :rtype: string
314 :return: A string containing the canonical user id. 343 :return: A string containing the canonical user id.
315 """ 344 """
316 rs = self.get_all_buckets(headers=headers) 345 rs = self.get_all_buckets(headers=headers)
317 return rs.ID 346 return rs.ID
318 347
319 def get_bucket(self, bucket_name, validate=True, headers=None): 348 def get_bucket(self, bucket_name, validate=True, headers=None):
320 bucket = self.bucket_class(self, bucket_name) 349 bucket = self.bucket_class(self, bucket_name)
321 if validate: 350 if validate:
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 host = self.calling_format.build_host(self.server_name(), bucket) 421 host = self.calling_format.build_host(self.server_name(), bucket)
393 if query_args: 422 if query_args:
394 path += '?' + query_args 423 path += '?' + query_args
395 boto.log.debug('path=%s' % path) 424 boto.log.debug('path=%s' % path)
396 auth_path += '?' + query_args 425 auth_path += '?' + query_args
397 boto.log.debug('auth_path=%s' % auth_path) 426 boto.log.debug('auth_path=%s' % auth_path)
398 return AWSAuthConnection.make_request(self, method, path, headers, 427 return AWSAuthConnection.make_request(self, method, path, headers,
399 data, host, auth_path, sender, 428 data, host, auth_path, sender,
400 override_num_retries=override_num_retries) 429 override_num_retries=override_num_retries)
401 430
OLDNEW
« no previous file with comments | « boto/s3/bucketlistresultset.py ('k') | boto/s3/key.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698