diff options
-rw-r--r-- | boto/cloudformation/connection.py | 40 | ||||
-rw-r--r-- | boto/cloudfront/__init__.py | 52 | ||||
-rw-r--r-- | boto/connection.py | 580 | ||||
-rw-r--r-- | boto/dynamodb/layer1.py | 17 | ||||
-rw-r--r-- | boto/ecs/__init__.py | 8 | ||||
-rw-r--r-- | boto/fps/connection.py | 70 | ||||
-rw-r--r-- | boto/gs/bucket.py | 12 | ||||
-rwxr-xr-x | boto/gs/connection.py | 10 | ||||
-rw-r--r-- | boto/gs/resumable_upload_handler.py | 2 | ||||
-rw-r--r-- | boto/iam/connection.py | 8 | ||||
-rw-r--r-- | boto/mturk/connection.py | 4 | ||||
-rw-r--r-- | boto/roboto/awsqueryrequest.py | 10 | ||||
-rw-r--r-- | boto/route53/connection.py | 42 | ||||
-rw-r--r-- | boto/s3/bucket.py | 255 | ||||
-rw-r--r-- | boto/s3/connection.py | 22 | ||||
-rw-r--r-- | boto/s3/key.py | 113 | ||||
-rw-r--r-- | boto/s3/multipart.py | 4 | ||||
-rw-r--r-- | boto/s3/resumable_download_handler.py | 2 | ||||
-rw-r--r-- | boto/sdb/connection.py | 8 | ||||
-rw-r--r-- | boto/ses/connection.py | 8 | ||||
-rw-r--r-- | boto/sns/connection.py | 104 | ||||
-rw-r--r-- | boto/utils.py | 2 | ||||
-rw-r--r-- | tests/s3/test_bucket.py | 12 |
23 files changed, 563 insertions, 822 deletions
diff --git a/boto/cloudformation/connection.py b/boto/cloudformation/connection.py index 6243dc6c..cd07d6b5 100644 --- a/boto/cloudformation/connection.py +++ b/boto/cloudformation/connection.py @@ -133,14 +133,14 @@ class CloudFormationConnection(AWSQueryConnection): params['TimeoutInMinutes'] = int(timeout_in_minutes) response = self.make_request('CreateStack', params, '/', 'POST') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: body = json.loads(body) return body['CreateStackResponse']['CreateStackResult']['StackId'] else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def update_stack(self, stack_name, template_body=None, template_url=None, parameters=[], notification_arns=[], disable_rollback=False, @@ -200,26 +200,26 @@ class CloudFormationConnection(AWSQueryConnection): params['TimeoutInMinutes'] = int(timeout_in_minutes) response = self.make_request('UpdateStack', params, '/', 'POST') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: body = json.loads(body) return body['UpdateStackResponse']['UpdateStackResult']['StackId'] else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def delete_stack(self, stack_name_or_id): params = {'ContentType': "JSON", 'StackName': stack_name_or_id} # TODO: change this to get_status ? response = self.make_request('DeleteStack', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def describe_stack_events(self, stack_name_or_id=None, next_token=None): params = {} @@ -235,13 +235,13 @@ class CloudFormationConnection(AWSQueryConnection): 'LogicalResourceId': logical_resource_id} response = self.make_request('DescribeStackResource', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def describe_stack_resources(self, stack_name_or_id=None, logical_resource_id=None, @@ -265,13 +265,13 @@ class CloudFormationConnection(AWSQueryConnection): def get_template(self, stack_name_or_id): params = {'ContentType': "JSON", 'StackName': stack_name_or_id} response = self.make_request('GetTemplate', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def list_stack_resources(self, stack_name_or_id, next_token=None): params = {'StackName': stack_name_or_id} diff --git a/boto/cloudfront/__init__.py b/boto/cloudfront/__init__.py index 7f98b70b..250958e6 100644 --- a/boto/cloudfront/__init__.py +++ b/boto/cloudfront/__init__.py @@ -47,7 +47,7 @@ class CloudFrontConnection(AWSAuthConnection): True, port, proxy, proxy_port, debug=debug) def get_etag(self, response): - response_headers = response.msg + response_headers = response.headers for key in response_headers.keys(): if key.lower() == 'etag': return response_headers[key] @@ -62,10 +62,10 @@ class CloudFrontConnection(AWSAuthConnection): if not tags: tags=[('DistributionSummary', DistributionSummary)] response = self.make_request('GET', '/%s/%s' % (self.Version, resource)) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise CloudFrontServerError(response.status, response.reason, body) + if response.status_code >= 300: + raise CloudFrontServerError(response.status_code, response.reason, body) rs = ResultSet(tags) h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) @@ -74,12 +74,12 @@ class CloudFrontConnection(AWSAuthConnection): def _get_info(self, id, resource, dist_class): uri = '/%s/%s/%s' % (self.Version, resource, id) response = self.make_request('GET', uri) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise CloudFrontServerError(response.status, response.reason, body) + if response.status_code >= 300: + raise CloudFrontServerError(response.status_code, response.reason, body) d = dist_class(connection=self) - response_headers = response.msg + response_headers = response.headers for key in response_headers.keys(): if key.lower() == 'etag': d.etag = response_headers[key] @@ -90,10 +90,10 @@ class CloudFrontConnection(AWSAuthConnection): def _get_config(self, id, resource, config_class): uri = '/%s/%s/%s/config' % (self.Version, resource, id) response = self.make_request('GET', uri) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise CloudFrontServerError(response.status, response.reason, body) + if response.status_code >= 300: + raise CloudFrontServerError(response.status_code, response.reason, body) d = config_class(connection=self) d.etag = self.get_etag(response) h = handler.XmlHandler(d, self) @@ -108,33 +108,33 @@ class CloudFrontConnection(AWSAuthConnection): uri = '/%s/%s/%s/config' % (self.Version, resource, distribution_id) headers = {'If-Match' : etag, 'Content-Type' : 'text/xml'} response = self.make_request('PUT', uri, headers, config.to_xml()) - body = response.read() + body = response.content boto.log.debug(body) - if response.status != 200: - raise CloudFrontServerError(response.status, response.reason, body) + if response.status_code != 200: + raise CloudFrontServerError(response.status_code, response.reason, body) return self.get_etag(response) def _create_object(self, config, resource, dist_class): response = self.make_request('POST', '/%s/%s' % (self.Version, resource), {'Content-Type' : 'text/xml'}, data=config.to_xml()) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 201: + if response.status_code == 201: d = dist_class(connection=self) h = handler.XmlHandler(d, self) xml.sax.parseString(body, h) d.etag = self.get_etag(response) return d else: - raise CloudFrontServerError(response.status, response.reason, body) + raise CloudFrontServerError(response.status_code, response.reason, body) def _delete_object(self, id, etag, resource): uri = '/%s/%s/%s' % (self.Version, resource, id) response = self.make_request('DELETE', uri, {'If-Match' : etag}) - body = response.read() + body = response.content boto.log.debug(body) - if response.status != 204: - raise CloudFrontServerError(response.status, response.reason, body) + if response.status_code != 204: + raise CloudFrontServerError(response.status_code, response.reason, body) # Distributions @@ -241,24 +241,24 @@ class CloudFrontConnection(AWSAuthConnection): response = self.make_request('POST', uri, {'Content-Type' : 'text/xml'}, data=paths.to_xml()) - body = response.read() - if response.status == 201: + body = response.content + if response.status_code == 201: h = handler.XmlHandler(paths, self) xml.sax.parseString(body, h) return paths else: - raise CloudFrontServerError(response.status, response.reason, body) + raise CloudFrontServerError(response.status_code, response.reason, body) def invalidation_request_status (self, distribution_id, request_id, caller_reference=None): uri = '/%s/distribution/%s/invalidation/%s' % (self.Version, distribution_id, request_id ) response = self.make_request('GET', uri, {'Content-Type' : 'text/xml'}) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: paths = InvalidationBatch([]) h = handler.XmlHandler(paths, self) xml.sax.parseString(body, h) return paths else: - raise CloudFrontServerError(response.status, response.reason, body) + raise CloudFrontServerError(response.status_code, response.reason, body) diff --git a/boto/connection.py b/boto/connection.py index e170d0b3..b8c3221b 100644 --- a/boto/connection.py +++ b/boto/connection.py @@ -43,7 +43,6 @@ Handles basic connections to AWS """ from __future__ import with_statement -import base64 import errno import httplib import os @@ -53,9 +52,11 @@ import re import socket import sys import time -import urllib, urlparse +import urlparse import xml.sax +import requests + import auth import auth_handler import boto @@ -78,10 +79,51 @@ try: except ImportError: pass -try: - import threading -except ImportError: - import dummy_threading as threading +# This is an ugly hack to bring over the sillyness that is HTTPResponse.reason. +# TODO: Try to get this from somewhere else? Can we pluck straight from +# httplib? Didn't have time to source dive. +HTTP_REASON_CODES = { + "100": "Continue", + "101": "Switching Protocols", + "200": "OK", + "201": "Created", + "202": "Accepted", + "203": "Non-Authoritative Information", + "204": "No Content", + "205": "Reset Content", + "206": "Partial Content", + "300": "Multiple Choices", + "301": "Moved Permanently", + "302": "Found", + "303": "See Other", + "304": "Not Modified", + "305": "Use Proxy", + "307": "Temporary Redirect", + "400": "Bad Request", + "401": "Unauthorized", + "402": "Payment Required", + "403": "Forbidden", + "404": "Not Found", + "405": "Method Not Allowed", + "406": "Not Acceptable", + "407": "Proxy Authentication Required", + "408": "Request Time-out", + "409": "Conflict", + "410": "Gone", + "411": "Length Required", + "412": "Precondition Failed", + "413": "Request Entity Too Large", + "414": "Request-URI Too Large", + "415": "Unsupported Media Type", + "416": "Requested range not satisfiable", + "417": "Expectation Failed", + "500": "Internal Server Error", + "501": "Not Implemented", + "502": "Bad Gateway", + "503": "Service Unavailable", + "504": "Gateway Time-out", + "505": "HTTP Version not supported", +} ON_APP_ENGINE = all(key in os.environ for key in ( 'USER_IS_ADMIN', 'CURRENT_VERSION_ID', 'APPLICATION_ID')) @@ -91,201 +133,12 @@ PORTS_BY_SECURITY = { True: 443, False: 80 } DEFAULT_CA_CERTS_FILE = os.path.join( os.path.dirname(os.path.abspath(boto.cacerts.__file__ )), "cacerts.txt") -class HostConnectionPool(object): - - """ - A pool of connections for one remote (host,is_secure). - - When connections are added to the pool, they are put into a - pending queue. The _mexe method returns connections to the pool - before the response body has been read, so they connections aren't - ready to send another request yet. They stay in the pending queue - until they are ready for another request, at which point they are - returned to the pool of ready connections. - - The pool of ready connections is an ordered list of - (connection,time) pairs, where the time is the time the connection - was returned from _mexe. After a certain period of time, - connections are considered stale, and discarded rather than being - reused. This saves having to wait for the connection to time out - if AWS has decided to close it on the other end because of - inactivity. - - Thread Safety: - - This class is used only fram ConnectionPool while it's mutex - is held. - """ - - def __init__(self): - self.queue = [] - - def size(self): - """ - Returns the number of connections in the pool for this host. - Some of the connections may still be in use, and may not be - ready to be returned by get(). - """ - return len(self.queue) - - def put(self, conn): - """ - Adds a connection to the pool, along with the time it was - added. - """ - self.queue.append((conn, time.time())) - - def get(self): - """ - Returns the next connection in this pool that is ready to be - reused. Returns None of there aren't any. - """ - # Discard ready connections that are too old. - self.clean() - - # Return the first connection that is ready, and remove it - # from the queue. Connections that aren't ready are returned - # to the end of the queue with an updated time, on the - # assumption that somebody is actively reading the response. - for _ in range(len(self.queue)): - (conn, _) = self.queue.pop(0) - if self._conn_ready(conn): - return conn - else: - self.put(conn) - return None - - def _conn_ready(self, conn): - """ - There is a nice state diagram at the top of httplib.py. It - indicates that once the response headers have been read (which - _mexe does before adding the connection to the pool), a - response is attached to the connection, and it stays there - until it's done reading. This isn't entirely true: even after - the client is done reading, the response may be closed, but - not removed from the connection yet. - - This is ugly, reading a private instance variable, but the - state we care about isn't available in any public methods. - """ - if ON_APP_ENGINE: - # Google App Engine implementation of HTTPConnection doesn't contain - # _HTTPConnection__response attribute. Moreover, it's not possible - # to determine if given connection is ready. Reusing connections - # simply doesn't make sense with App Engine urlfetch service. - return False - else: - response = getattr(conn, '_HTTPConnection__response', None) - return (response is None) or response.isclosed() - - def clean(self): - """ - Get rid of stale connections. - """ - # Note that we do not close the connection here -- somebody - # may still be reading from it. - while len(self.queue) > 0 and self._pair_stale(self.queue[0]): - self.queue.pop(0) - - def _pair_stale(self, pair): - """ - Returns true of the (connection,time) pair is too old to be - used. - """ - (_conn, return_time) = pair - now = time.time() - return return_time + ConnectionPool.STALE_DURATION < now - -class ConnectionPool(object): - +class HTTPRequest(object): """ - A connection pool that expires connections after a fixed period of - time. This saves time spent waiting for a connection that AWS has - timed out on the other end. - - This class is thread-safe. + A data encapsulation class used for passing values to our HTTP client. + This is currently the 'requests' library. """ - # - # The amout of time between calls to clean. - # - - CLEAN_INTERVAL = 5.0 - - # - # How long before a connection becomes "stale" and won't be reused - # again. The intention is that this time is less that the timeout - # period that AWS uses, so we'll never try to reuse a connection - # and find that AWS is timing it out. - # - # Experimentation in July 2011 shows that AWS starts timing things - # out after three minutes. The 60 seconds here is conservative so - # we should never hit that 3-minute timout. - # - - STALE_DURATION = 60.0 - - def __init__(self): - # Mapping from (host,is_secure) to HostConnectionPool. - # If a pool becomes empty, it is removed. - self.host_to_pool = {} - # The last time the pool was cleaned. - self.last_clean_time = 0.0 - self.mutex = threading.Lock() - - def size(self): - """ - Returns the number of connections in the pool. - """ - return sum(pool.size() for pool in self.host_to_pool.values()) - - def get_http_connection(self, host, is_secure): - """ - Gets a connection from the pool for the named host. Returns - None if there is no connection that can be reused. It's the caller's - responsibility to call close() on the connection when it's no longer - needed. - """ - self.clean() - with self.mutex: - key = (host, is_secure) - if key not in self.host_to_pool: - return None - return self.host_to_pool[key].get() - - def put_http_connection(self, host, is_secure, conn): - """ - Adds a connection to the pool of connections that can be - reused for the named host. - """ - with self.mutex: - key = (host, is_secure) - if key not in self.host_to_pool: - self.host_to_pool[key] = HostConnectionPool() - self.host_to_pool[key].put(conn) - - def clean(self): - """ - Clean up the stale connections in all of the pools, and then - get rid of empty pools. Pools clean themselves every time a - connection is fetched; this cleaning takes care of pools that - aren't being used any more, so nothing is being gotten from - them. - """ - with self.mutex: - now = time.time() - if self.last_clean_time + self.CLEAN_INTERVAL < now: - to_remove = [] - for (host, pool) in self.host_to_pool.items(): - pool.clean() - if pool.size() == 0: - to_remove.append(host) - for host in to_remove: - del self.host_to_pool[host] - self.last_clean_time = now - -class HTTPRequest(object): - def __init__(self, method, protocol, host, port, path, auth_path, params, headers, body): """Represents an HTTP request. @@ -327,32 +180,26 @@ class HTTPRequest(object): self.host = host self.port = port self.path = path - if auth_path is None: - auth_path = path - self.auth_path = auth_path + self.auth_path = auth_path or path self.params = params + self.body = body + # chunked Transfer-Encoding should act only on PUT request. if headers and 'Transfer-Encoding' in headers and \ - headers['Transfer-Encoding'] == 'chunked' and \ - self.method != 'PUT': + headers['Transfer-Encoding'] == 'chunked' and \ + self.method != 'PUT': self.headers = headers.copy() del self.headers['Transfer-Encoding'] else: self.headers = headers - self.body = body def __str__(self): return (('method:(%s) protocol:(%s) host(%s) port(%s) path(%s) ' - 'params(%s) headers(%s) body(%s)') % (self.method, - self.protocol, self.host, self.port, self.path, self.params, - self.headers, self.body)) + 'params(%s) headers(%s) body(%s)') % ( + self.method, self.protocol, self.host, self.port, self.path, + self.params, self.headers, self.body)) def authorize(self, connection, **kwargs): - for key in self.headers: - val = self.headers[key] - if isinstance(val, unicode): - self.headers[key] = urllib.quote_plus(val.encode('utf-8')) - connection._auth_handler.add_auth(self, **kwargs) self.headers['User-Agent'] = UserAgent @@ -427,15 +274,18 @@ class AWSAuthConnection(object): "2.6 or later.") self.ca_certificates_file = config.get_value( 'Boto', 'ca_certificates_file', DEFAULT_CA_CERTS_FILE) - self.handle_proxy(proxy, proxy_port, proxy_user, proxy_pass) # define exceptions from httplib that we want to catch and retry - self.http_exceptions = (httplib.HTTPException, socket.error, - socket.gaierror) + # TODO: Update for requests. + self.http_exceptions = ( + httplib.HTTPException, + socket.error, + socket.gaierror + ) # define subclasses of the above that are not retryable. self.http_unretryable_exceptions = [] if HAVE_HTTPS_CONNECTION: self.http_unretryable_exceptions.append( - https_connection.InvalidCertificateException) + requests.exceptions.SSLError) # define values in socket exceptions we don't want to catch self.socket_exception_values = (errno.EINTR,) @@ -444,7 +294,7 @@ class AWSAuthConnection(object): self.http_exceptions += https_connection_factory[1] else: self.https_connection_factory = None - if (is_secure): + if is_secure: self.protocol = 'https' else: self.protocol = 'http' @@ -459,16 +309,10 @@ class AWSAuthConnection(object): else: self.port = PORTS_BY_SECURITY[is_secure] - # Timeout used to tell httplib how long to wait for socket timeouts. - # Default is to leave timeout unchanged, which will in turn result in - # the socket's default global timeout being used. To specify a - # timeout, set http_socket_timeout in Boto config. Regardless, - # timeouts will only be applied if Python is 2.6 or greater. - self.http_connection_kwargs = {} - if (sys.version_info[0], sys.version_info[1]) >= (2, 6): - if config.has_option('Boto', 'http_socket_timeout'): - timeout = config.getint('Boto', 'http_socket_timeout') - self.http_connection_kwargs['timeout'] = timeout + if config.has_option('Boto', 'http_socket_timeout'): + self.timeout = config.getint('Boto', 'http_socket_timeout') + else: + self.timeout = None self.provider = Provider(provider, aws_access_key_id, @@ -479,7 +323,6 @@ class AWSAuthConnection(object): if self.provider.host: self.host = self.provider.host - self._pool = ConnectionPool() self._connection = (self.server_name(), self.is_secure) self._last_rs = None self._auth_handler = auth.get_auth_handler( @@ -491,10 +334,6 @@ class AWSAuthConnection(object): def _required_auth_capability(self): return [] - def connection(self): - return self.get_http_connection(*self._connection) - connection = property(connection) - def aws_access_key_id(self): return self.provider.access_key aws_access_key_id = property(aws_access_key_id) @@ -554,148 +393,6 @@ class AWSAuthConnection(object): signature_host = '%s:%d' % (self.host, port) return signature_host - def handle_proxy(self, proxy, proxy_port, proxy_user, proxy_pass): - self.proxy = proxy - self.proxy_port = proxy_port - self.proxy_user = proxy_user - self.proxy_pass = proxy_pass - if os.environ.has_key('http_proxy') and not self.proxy: - pattern = re.compile( - '(?:http://)?' \ - '(?:(?P<user>\w+):(?P<pass>.*)@)?' \ - '(?P<host>[\w\-\.]+)' \ - '(?::(?P<port>\d+))?' - ) - match = pattern.match(os.environ['http_proxy']) - if match: - self.proxy = match.group('host') - self.proxy_port = match.group('port') - self.proxy_user = match.group('user') - self.proxy_pass = match.group('pass') - else: - if not self.proxy: - self.proxy = config.get_value('Boto', 'proxy', None) - if not self.proxy_port: - self.proxy_port = config.get_value('Boto', 'proxy_port', None) - if not self.proxy_user: - self.proxy_user = config.get_value('Boto', 'proxy_user', None) - if not self.proxy_pass: - self.proxy_pass = config.get_value('Boto', 'proxy_pass', None) - - if not self.proxy_port and self.proxy: - print "http_proxy environment variable does not specify " \ - "a port, using default" - self.proxy_port = self.port - self.use_proxy = (self.proxy != None) - - def get_http_connection(self, host, is_secure): - conn = self._pool.get_http_connection(host, is_secure) - if conn is not None: - return conn - else: - return self.new_http_connection(host, is_secure) - - def new_http_connection(self, host, is_secure): - if self.use_proxy: - host = '%s:%d' % (self.proxy, int(self.proxy_port)) - if host is None: - host = self.server_name() - if is_secure: - boto.log.debug( - 'establishing HTTPS connection: host=%s, kwargs=%s', - host, self.http_connection_kwargs) - if self.use_proxy: - connection = self.proxy_ssl() - elif self.https_connection_factory: - connection = self.https_connection_factory(host) - elif self.https_validate_certificates and HAVE_HTTPS_CONNECTION: - connection = https_connection.CertValidatingHTTPSConnection( - host, ca_certs=self.ca_certificates_file, - **self.http_connection_kwargs) - else: - connection = httplib.HTTPSConnection(host, - **self.http_connection_kwargs) - else: - boto.log.debug('establishing HTTP connection: kwargs=%s' % - self.http_connection_kwargs) - connection = httplib.HTTPConnection(host, - **self.http_connection_kwargs) - if self.debug > 1: - connection.set_debuglevel(self.debug) - # self.connection must be maintained for backwards-compatibility - # however, it must be dynamically pulled from the connection pool - # set a private variable which will enable that - if host.split(':')[0] == self.host and is_secure == self.is_secure: - self._connection = (host, is_secure) - return connection - - def put_http_connection(self, host, is_secure, connection): - self._pool.put_http_connection(host, is_secure, connection) - - def proxy_ssl(self): - host = '%s:%d' % (self.host, self.port) - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect((self.proxy, int(self.proxy_port))) - except: - raise - boto.log.debug("Proxy connection: CONNECT %s HTTP/1.0\r\n", host) - sock.sendall("CONNECT %s HTTP/1.0\r\n" % host) - sock.sendall("User-Agent: %s\r\n" % UserAgent) - if self.proxy_user and self.proxy_pass: - for k, v in self.get_proxy_auth_header().items(): - sock.sendall("%s: %s\r\n" % (k, v)) - sock.sendall("\r\n") - resp = httplib.HTTPResponse(sock, strict=True, debuglevel=self.debug) - resp.begin() - - if resp.status != 200: - # Fake a socket error, use a code that make it obvious it hasn't - # been generated by the socket library - raise socket.error(-71, - "Error talking to HTTP proxy %s:%s: %s (%s)" % - (self.proxy, self.proxy_port, resp.status, resp.reason)) - - # We can safely close the response, it duped the original socket - resp.close() - - h = httplib.HTTPConnection(host) - - if self.https_validate_certificates and HAVE_HTTPS_CONNECTION: - boto.log.debug("wrapping ssl socket for proxied connection; " - "CA certificate file=%s", - self.ca_certificates_file) - key_file = self.http_connection_kwargs.get('key_file', None) - cert_file = self.http_connection_kwargs.get('cert_file', None) - sslSock = ssl.wrap_socket(sock, keyfile=key_file, - certfile=cert_file, - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=self.ca_certificates_file) - cert = sslSock.getpeercert() - hostname = self.host.split(':', 0)[0] - if not https_connection.ValidateCertificateHostname(cert, hostname): - raise https_connection.InvalidCertificateException( - hostname, cert, 'hostname mismatch') - else: - # Fallback for old Python without ssl.wrap_socket - if hasattr(httplib, 'ssl'): - sslSock = httplib.ssl.SSLSocket(sock) - else: - sslSock = socket.ssl(sock, None, None) - sslSock = httplib.FakeSocket(sock, sslSock) - - # This is a bit unclean - h.sock = sslSock - return h - - def prefix_proxy_to_path(self, path, host=None): - path = self.protocol + '://' + (host or self.server_name()) + path - return path - - def get_proxy_auth_header(self): - auth = base64.encodestring(self.proxy_user + ':' + self.proxy_pass) - return {'Proxy-Authorization': 'Basic %s' % auth} - def _mexe(self, request, sender=None, override_num_retries=None, retry_handler=None): """ @@ -707,11 +404,12 @@ class AWSAuthConnection(object): Google group by Larry Bates. Thanks! """ + boto.log.debug('') + boto.log.debug('>>>>>> Request Details >>>>>>') boto.log.debug('Method: %s' % request.method) boto.log.debug('Path: %s' % request.path) - boto.log.debug('Data: %s' % request.body) - boto.log.debug('Headers: %s' % request.headers) boto.log.debug('Host: %s' % request.host) + response = None body = None e = None @@ -720,28 +418,62 @@ class AWSAuthConnection(object): else: num_retries = override_num_retries i = 0 - connection = self.get_http_connection(request.host, self.is_secure) + while i <= num_retries: - # Use binary exponential backoff to desynchronize client requests + # Use binary exponential backoff to de-synchronize client requests next_sleep = random.random() * (2 ** i) try: # we now re-sign each request before it is retried boto.log.debug('Token: %s' % self.provider.security_token) request.authorize(connection=self) - if callable(sender): - response = sender(connection, request.method, request.path, - request.body, request.headers) + + url = '%s://%s%s' % ( + request.protocol, + request.host, + request.path + ) + + # This is really, really explicit, but meh. + if request.method == 'GET': + request_method = requests.get + elif request.method == 'POST': + request_method = requests.post + elif request.method == 'HEAD': + request_method = requests.head + elif request.method == 'PUT': + request_method = requests.put + elif request.method == 'DELETE': + request_method = requests.delete else: - connection.request(request.method, request.path, - request.body, request.headers) - response = connection.getresponse() - location = response.getheader('location') - # -- gross hack -- - # httplib gets confused with chunked responses to HEAD requests - # so I have to fake it out - if request.method == 'HEAD' and getattr(response, - 'chunked', False): - response.chunked = 0 + raise BotoClientError( + "Unrecognized HTTP method: %s" % request.method + ) + + response = request_method( + url, + data=request.body, + headers=request.headers, + verify=self.https_validate_certificates, + timeout=self.timeout, + #config={'verbose': sys.stderr}, + ) + boto.log.debug('Headers: %s' % response.request.headers) + boto.log.debug('Params: %s' % response.request.params) + boto.log.debug('Data: %s' % request.body) + + status_reason = HTTP_REASON_CODES.get(response.status_code, 'Unknown') + setattr(response, 'reason', status_reason) + + boto.log.debug('') + boto.log.debug('<<<<<< Response Details <<<<<<') + boto.log.debug('Status: %s' % response.status_code) + boto.log.debug('Headers: %s' % response.headers) + boto.log.debug('Content: %s' % response.content) + boto.log.debug('<' * 30) + boto.log.debug('') + + location = response.headers['location'] + if callable(retry_handler): status = retry_handler(response, i, next_sleep) if status: @@ -750,17 +482,19 @@ class AWSAuthConnection(object): boto.log.debug(msg) time.sleep(next_sleep) continue - if response.status == 500 or response.status == 503: - msg = 'Received %d response. ' % response.status + if response.status_code == 500 or response.status_code == 503: + msg = 'Received %d response. ' % response.status_code msg += 'Retrying in %3.1f seconds' % next_sleep boto.log.debug(msg) - body = response.read() - elif response.status < 300 or response.status >= 400 or \ - not location: - self.put_http_connection(request.host, self.is_secure, - connection) + body = response.text + elif response.status_code < 300 or \ + response.status_code >= 400 or \ + not location: + # Success. return response else: + # Not sure what happened, appears to be a redir. Grab + # the components out of the 'location' header value. scheme, request.host, request.path, \ params, query, fragment = urlparse.urlparse(location) if query: @@ -768,8 +502,7 @@ class AWSAuthConnection(object): msg = 'Redirecting: %s' % scheme + '://' msg += request.host + request.path boto.log.debug(msg) - connection = self.get_http_connection(request.host, - scheme == 'https') + # Fall through to another re-try with the modified values. continue except self.http_exceptions, e: for unretryable in self.http_unretryable_exceptions: @@ -780,8 +513,6 @@ class AWSAuthConnection(object): raise e boto.log.debug('encountered %s exception, reconnecting' % \ e.__class__.__name__) - connection = self.new_http_connection(request.host, - self.is_secure) time.sleep(next_sleep) i += 1 # If we made it here, it's because we have exhausted our retries @@ -789,7 +520,7 @@ class AWSAuthConnection(object): # use it to raise an exception. # Otherwise, raise the exception that must have already h#appened. if response: - raise BotoServerError(response.status, response.reason, body) + raise BotoServerError(response.status_code, status.reason, body) elif e: raise e else: @@ -801,23 +532,16 @@ class AWSAuthConnection(object): path = self.get_path(path) if auth_path is not None: auth_path = self.get_path(auth_path) - if params == None: + if params is None: params = {} else: params = params.copy() - if headers == None: + if headers is None: headers = {} else: headers = headers.copy() host = host or self.host - if self.use_proxy: - if not auth_path: - auth_path = path - path = self.prefix_proxy_to_path(path, host) - if self.proxy_user and self.proxy_pass and not self.is_secure: - # If is_secure, we don't have to set the proxy authentication - # header here, we did that in the CONNECT to the proxy. - headers.update(self.get_proxy_auth_header()) + return HTTPRequest(method, self.protocol, host, self.port, path, auth_path, params, headers, data) @@ -880,56 +604,56 @@ class AWSQueryConnection(AWSAuthConnection): if not parent: parent = self response = self.make_request(action, params, path, verb) - body = response.read() + body = response.content boto.log.debug(body) if not body: boto.log.error('Null body %s' % body) - raise self.ResponseError(response.status, response.reason, body) - elif response.status == 200: + raise self.ResponseError(response.status_code, response.reason, body) + elif response.status_code == 200: rs = ResultSet(markers) h = boto.handler.XmlHandler(rs, parent) xml.sax.parseString(body, h) return rs else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def get_object(self, action, params, cls, path='/', parent=None, verb='GET'): if not parent: parent = self response = self.make_request(action, params, path, verb) - body = response.read() + body = response.content boto.log.debug(body) if not body: boto.log.error('Null body %s' % body) - raise self.ResponseError(response.status, response.reason, body) - elif response.status == 200: + raise self.ResponseError(response.status_code, response.reason, body) + elif response.status_code == 200: obj = cls(parent) h = boto.handler.XmlHandler(obj, parent) xml.sax.parseString(body, h) return obj else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def get_status(self, action, params, path='/', parent=None, verb='GET'): if not parent: parent = self response = self.make_request(action, params, path, verb) - body = response.read() + body = response.content boto.log.debug(body) if not body: boto.log.error('Null body %s' % body) - raise self.ResponseError(response.status, response.reason, body) - elif response.status == 200: + raise self.ResponseError(response.status_code, response.reason, body) + elif response.status_code == 200: rs = ResultSet() h = boto.handler.XmlHandler(rs, parent) xml.sax.parseString(body, h) return rs.status else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) diff --git a/boto/dynamodb/layer1.py b/boto/dynamodb/layer1.py index d7b27f41..2ff77ce4 100644 --- a/boto/dynamodb/layer1.py +++ b/boto/dynamodb/layer1.py @@ -110,23 +110,24 @@ class Layer1(AWSAuthConnection): """ :raises: ``DynamoDBExpiredTokenError`` if the security token expires. """ - headers = {'X-Amz-Target' : '%s_%s.%s' % (self.ServiceName, - self.Version, action), - 'Content-Type' : 'application/x-amz-json-1.0', - 'Content-Length' : str(len(body))} + headers = { + 'X-Amz-Target' : '%s_%s.%s' % (self.ServiceName, self.Version, action), + 'Content-Type' : 'application/x-amz-json-1.0', + 'Content-Length' : str(len(body)), + } http_request = self.build_base_http_request('POST', '/', '/', {}, headers, body, None) response = self._mexe(http_request, sender=None, override_num_retries=10, retry_handler=self._retry_handler) - response_body = response.read() + response_body = response.content boto.log.debug(response_body) return json.loads(response_body, object_hook=object_hook) def _retry_handler(self, response, i, next_sleep): status = None - if response.status == 400: - response_body = response.read() + if response.status_code == 400: + response_body = response.content boto.log.debug(response_body) json_response = json.loads(response_body) if self.ThruputError in json_response.get('__type'): @@ -144,7 +145,7 @@ class Layer1(AWSAuthConnection): self._update_provider() status = (msg, i+self.num_retries-1, next_sleep) else: - raise self.ResponseError(response.status, response.reason, + raise self.ResponseError(response.status_code, response.reason, json_response) return status diff --git a/boto/ecs/__init__.py b/boto/ecs/__init__.py index cbaf478a..abf298ad 100644 --- a/boto/ecs/__init__.py +++ b/boto/ecs/__init__.py @@ -58,13 +58,13 @@ class ECSConnection(AWSQueryConnection): if page: params['ItemPage'] = page response = self.make_request(None, params, "/onca/xml") - body = response.read() + body = response.content boto.log.debug(body) - if response.status != 200: - boto.log.error('%s %s' % (response.status, response.reason)) + if response.status_code != 200: + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) if itemSet == None: rs = ItemSet(self, action, params, page) diff --git a/boto/fps/connection.py b/boto/fps/connection.py index 24b04d91..6094e003 100644 --- a/boto/fps/connection.py +++ b/boto/fps/connection.py @@ -83,8 +83,8 @@ class FPSConnection(AWSQueryConnection): response = self.install_payment_instruction("MyRole=='Caller';", token_type=token_type, transaction_id=transaction_id) - body = response.read() - if(response.status == 200): + body = response.content + if response.status_code == 200: rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) @@ -92,12 +92,12 @@ class FPSConnection(AWSQueryConnection): try: boto.config.save_system_option("FPS", "caller_token", caller_token) - except(IOError): + except IOError: boto.config.save_user_option("FPS", "caller_token", caller_token) return caller_token else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def install_recipient_instruction(self, token_type="Unrestricted", transaction_id=None): @@ -109,8 +109,8 @@ class FPSConnection(AWSQueryConnection): response = self.install_payment_instruction("MyRole=='Recipient';", token_type=token_type, transaction_id=transaction_id) - body = response.read() - if(response.status == 200): + body = response.content + if response.status_code == 200: rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) @@ -118,13 +118,13 @@ class FPSConnection(AWSQueryConnection): try: boto.config.save_system_option("FPS", "recipient_token", recipient_token) - except(IOError): + except IOError: boto.config.save_user_option("FPS", "recipient_token", recipient_token) return recipient_token else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def make_marketplace_registration_url(self, returnURL, pipelineName, maxFixedFee=0.0, maxVariableFee=0.0, @@ -266,14 +266,14 @@ class FPSConnection(AWSQueryConnection): response = self.make_request("Reserve", params) else: response = self.make_request("Pay", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def get_transaction_status(self, transactionId): """ @@ -283,14 +283,14 @@ class FPSConnection(AWSQueryConnection): params['TransactionId'] = transactionId response = self.make_request("GetTransactionStatus", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def cancel(self, transactionId, description=None): """ @@ -302,14 +302,14 @@ class FPSConnection(AWSQueryConnection): params['description'] = description response = self.make_request("Cancel", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def settle(self, reserveTransactionId, transactionAmount=None): """ @@ -321,14 +321,14 @@ class FPSConnection(AWSQueryConnection): params['TransactionAmount'] = transactionAmount response = self.make_request("Settle", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def refund(self, callerReference, transactionId, refundAmount=None, callerDescription=None): @@ -345,14 +345,14 @@ class FPSConnection(AWSQueryConnection): params['CallerDescription'] = callerDescription response = self.make_request("Refund", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def get_recipient_verification_status(self, recipientTokenId): """ @@ -362,14 +362,14 @@ class FPSConnection(AWSQueryConnection): params['RecipientTokenId'] = recipientTokenId response = self.make_request("GetRecipientVerificationStatus", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def get_token_by_caller_reference(self, callerReference): """ @@ -379,14 +379,14 @@ class FPSConnection(AWSQueryConnection): params['CallerReference'] = callerReference response = self.make_request("GetTokenByCaller", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def get_token_by_caller_token(self, tokenId): """ @@ -396,14 +396,14 @@ class FPSConnection(AWSQueryConnection): params['TokenId'] = tokenId response = self.make_request("GetTokenByCaller", params) - body = response.read() - if(response.status == 200): + body = response.content + if(response.status_code == 200): rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: - raise FPSResponseError(response.status, response.reason, body) + raise FPSResponseError(response.status_code, response.reason, body) def verify_signature(self, end_point_url, http_parameters): params = dict( @@ -411,9 +411,9 @@ class FPSConnection(AWSQueryConnection): HttpParameters = http_parameters, ) response = self.make_request("VerifySignature", params) - body = response.read() - if(response.status != 200): - raise FPSResponseError(response.status, response.reason, body) + body = response.content + if(response.status_code != 200): + raise FPSResponseError(response.status_code, response.reason, body) rs = ResultSet() h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) diff --git a/boto/gs/bucket.py b/boto/gs/bucket.py index aa2d4c23..869a42c4 100644 --- a/boto/gs/bucket.py +++ b/boto/gs/bucket.py @@ -64,15 +64,15 @@ class Bucket(S3Bucket): response = self.connection.make_request('GET', self.name, key_name, query_args=query_args, headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: acl = ACL(self) h = handler.XmlHandler(acl, self) xml.sax.parseString(body, h) return acl else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_acl(self, key_name='', headers=None, version_id=None): """returns a bucket's acl. We include a version_id argument @@ -97,10 +97,10 @@ class Bucket(S3Bucket): response = self.connection.make_request('PUT', self.name, key_name, headers=headers, query_args=query_args) - body = response.read() - if response.status != 200: + body = response.content + if response.status_code != 200: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def set_canned_acl(self, acl_str, key_name='', headers=None, version_id=None): diff --git a/boto/gs/connection.py b/boto/gs/connection.py index 20b02203..59a6f8ae 100755 --- a/boto/gs/connection.py +++ b/boto/gs/connection.py @@ -82,13 +82,13 @@ class GSConnection(S3Connection): '</CreateBucketConfiguration>' % location) response = self.make_request('PUT', bucket_name, headers=headers, data=data) - body = response.read() - if response.status == 409: + body = response.content + if response.status_code == 409: raise self.provider.storage_create_error( - response.status, response.reason, body) - if response.status == 200: + response.status_code, response.reason, body) + if response.status_code == 200: return self.bucket_class(self, bucket_name) else: raise self.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) diff --git a/boto/gs/resumable_upload_handler.py b/boto/gs/resumable_upload_handler.py index 3959565a..93185a92 100644 --- a/boto/gs/resumable_upload_handler.py +++ b/boto/gs/resumable_upload_handler.py @@ -234,7 +234,7 @@ class ResumableUploadHandler(object): if not got_valid_response: raise ResumableUploadException( 'Couldn\'t parse upload server state query response (%s)' % - str(resp.getheaders()), ResumableTransferDisposition.START_OVER) + str(resp.headers), ResumableTransferDisposition.START_OVER) if conn.debug >= 1: print 'Server has: Range: %d - %d.' % (server_start, server_end) return (server_start, server_end) diff --git a/boto/iam/connection.py b/boto/iam/connection.py index ae68f337..5d8c3c55 100644 --- a/boto/iam/connection.py +++ b/boto/iam/connection.py @@ -54,18 +54,18 @@ class IAMConnection(AWSQueryConnection): if not parent: parent = self response = self.make_request(action, params, path, verb) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 200: + if response.status_code == 200: e = boto.jsonresponse.Element(list_marker=list_marker, pythonize_name=True) h = boto.jsonresponse.XmlHandler(e, parent) h.parse(body) return e else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) # # Group methods diff --git a/boto/mturk/connection.py b/boto/mturk/connection.py index ecd3318e..65e01073 100644 --- a/boto/mturk/connection.py +++ b/boto/mturk/connection.py @@ -735,7 +735,7 @@ class MTurkConnection(AWSQueryConnection): """ Helper to process the xml response from AWS """ - body = response.read() + body = response.content #print body if '<Errors>' not in body: rs = ResultSet(marker_elems) @@ -743,7 +743,7 @@ class MTurkConnection(AWSQueryConnection): xml.sax.parseString(body, h) return rs else: - raise MTurkRequestError(response.status, response.reason, body) + raise MTurkRequestError(response.status_code, response.reason, body) @staticmethod def get_keywords_as_string(keywords): diff --git a/boto/roboto/awsqueryrequest.py b/boto/roboto/awsqueryrequest.py index 9e05ac63..03d95d44 100644 --- a/boto/roboto/awsqueryrequest.py +++ b/boto/roboto/awsqueryrequest.py @@ -201,7 +201,7 @@ class AWSQueryRequest(object): def status(self): retval = None if self.http_response is not None: - retval = self.http_response.status + retval = self.http_response.status_code return retval @property @@ -298,19 +298,19 @@ class AWSQueryRequest(object): self.http_response = conn.make_request(self.name(), self.request_params, verb=verb) - self.body = self.http_response.read() + self.body = self.http_response.response boto.log.debug(self.body) - if self.http_response.status == 200: + if self.http_response.status_code == 200: self.aws_response = boto.jsonresponse.Element(list_marker=self.list_markers, item_marker=self.item_markers) h = boto.jsonresponse.XmlHandler(self.aws_response, self) h.parse(self.body) return self.aws_response else: - boto.log.error('%s %s' % (self.http_response.status, + boto.log.error('%s %s' % (self.http_response.status_code, self.http_response.reason)) boto.log.error('%s' % self.body) - raise conn.ResponseError(self.http_response.status, + raise conn.ResponseError(self.http_response.status_code, self.http_response.reason, self.body) diff --git a/boto/route53/connection.py b/boto/route53/connection.py index 7c3f1b88..64ecd420 100644 --- a/boto/route53/connection.py +++ b/boto/route53/connection.py @@ -89,10 +89,10 @@ class Route53Connection(AWSAuthConnection): params = {'marker': start_marker} response = self.make_request('GET', '/%s/hostedzone' % self.Version, params=params) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise exception.DNSServerError(response.status, + if response.status_code >= 300: + raise exception.DNSServerError(response.status_code, response.reason, body) e = boto.jsonresponse.Element(list_marker='HostedZones', @@ -117,10 +117,10 @@ class Route53Connection(AWSAuthConnection): """ uri = '/%s/hostedzone/%s' % (self.Version, hosted_zone_id) response = self.make_request('GET', uri) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise exception.DNSServerError(response.status, + if response.status_code >= 300: + raise exception.DNSServerError(response.status_code, response.reason, body) e = boto.jsonresponse.Element(list_marker='NameServers', @@ -167,26 +167,26 @@ class Route53Connection(AWSAuthConnection): uri = '/%s/hostedzone' % self.Version response = self.make_request('POST', uri, {'Content-Type' : 'text/xml'}, xml) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 201: + if response.status_code == 201: e = boto.jsonresponse.Element(list_marker='NameServers', item_marker=('NameServer',)) h = boto.jsonresponse.XmlHandler(e, None) h.parse(body) return e else: - raise exception.DNSServerError(response.status, + raise exception.DNSServerError(response.status_code, response.reason, body) def delete_hosted_zone(self, hosted_zone_id): uri = '/%s/hostedzone/%s' % (self.Version, hosted_zone_id) response = self.make_request('DELETE', uri) - body = response.read() + body = response.content boto.log.debug(body) - if response.status not in (200, 204): - raise exception.DNSServerError(response.status, + if response.status_code not in (200, 204): + raise exception.DNSServerError(response.status_code, response.reason, body) e = boto.jsonresponse.Element() @@ -253,10 +253,10 @@ class Route53Connection(AWSAuthConnection): 'Identifier': identifier, 'maxitems': maxitems} uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id) response = self.make_request('GET', uri, params=params) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise exception.DNSServerError(response.status, + if response.status_code >= 300: + raise exception.DNSServerError(response.status_code, response.reason, body) rs = ResourceRecordSets(connection=self, hosted_zone_id=hosted_zone_id) @@ -283,10 +283,10 @@ class Route53Connection(AWSAuthConnection): response = self.make_request('POST', uri, {'Content-Type' : 'text/xml'}, xml_body) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise exception.DNSServerError(response.status, + if response.status_code >= 300: + raise exception.DNSServerError(response.status_code, response.reason, body) e = boto.jsonresponse.Element() @@ -308,10 +308,10 @@ class Route53Connection(AWSAuthConnection): """ uri = '/%s/change/%s' % (self.Version, change_id) response = self.make_request('GET', uri) - body = response.read() + body = response.content boto.log.debug(body) - if response.status >= 300: - raise exception.DNSServerError(response.status, + if response.status_code >= 300: + raise exception.DNSServerError(response.status_code, response.reason, body) e = boto.jsonresponse.Element() diff --git a/boto/s3/bucket.py b/boto/s3/bucket.py index a1f0730a..25325a07 100644 --- a/boto/s3/bucket.py +++ b/boto/s3/bucket.py @@ -44,7 +44,6 @@ import xml.sax.saxutils import StringIO import urllib import re -import base64 from collections import defaultdict # as per http://goo.gl/BDuud (02/19/2011) @@ -59,8 +58,8 @@ class S3WebsiteEndpointTranslate: trans_region['ap-southeast-1'] = 's3-website-ap-southeast-1' @classmethod - def translate_region(self, reg): - return self.trans_region[reg] + def translate_region(cls, reg): + return cls.trans_region[reg] S3Permissions = ['READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'] @@ -169,41 +168,43 @@ class Bucket(object): query_args = 'versionId=%s' % version_id else: query_args = None - response = self.connection.make_request('HEAD', self.name, key_name, + response = self.connection.make_request('HEAD', + bucket=self.name, + key=key_name, headers=headers, query_args=query_args) # Allow any success status (2xx) - for example this lets us # support Range gets, which return status 206: - if response.status/100 == 2: - response.read() + if response.status_code / 100 == 2: + response.content k = self.key_class(self) provider = self.connection.provider - k.metadata = boto.utils.get_aws_metadata(response.msg, provider) - k.etag = response.getheader('etag') - k.content_type = response.getheader('content-type') - k.content_encoding = response.getheader('content-encoding') - k.last_modified = response.getheader('last-modified') + k.metadata = boto.utils.get_aws_metadata(response.headers, provider) + k.etag = response.headers.get('etag') + k.content_type = response.headers.get('content-type') + k.content_encoding = response.headers.get('content-encoding') + k.last_modified = response.headers.get('last-modified') # the following machinations are a workaround to the fact that # apache/fastcgi omits the content-length header on HEAD # requests when the content-length is zero. # See http://goo.gl/0Tdax for more details. - clen = response.getheader('content-length') + clen = response.headers.get('content-length') if clen: - k.size = int(response.getheader('content-length')) + k.size = int(response.headers.get('content-length')) else: k.size = 0 - k.cache_control = response.getheader('cache-control') + k.cache_control = response.headers.get('cache-control') k.name = key_name k.handle_version_headers(response) k.handle_encryption_headers(response) return k else: - if response.status == 404: - response.read() + if response.status_code == 404: + response.content return None else: raise self.connection.provider.storage_response_error( - response.status, response.reason, '') + response.status_code, response.reason, '') def list(self, prefix='', delimiter='', marker='', headers=None): """ @@ -267,8 +268,8 @@ class Bucket(object): http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ for more details. - :type marker: string - :param marker: The "marker" of where you are in the result set + :type key_marker: string + :param key_marker: The "marker" of where you are in the result set :rtype: :class:`boto.s3.bucketlistresultset.BucketListResultSet` :return: an instance of a BucketListResultSet that handles paging, etc @@ -285,8 +286,8 @@ class Bucket(object): handles all of the result paging, etc. from S3. You just need to keep iterating until there are no more results. - :type marker: string - :param marker: The "marker" of where you are in the result set + :type key_marker: string + :param key_marker: The "marker" of where you are in the result set :rtype: :class:`boto.s3.bucketlistresultset.BucketListResultSet` :return: an instance of a BucketListResultSet that handles paging, etc @@ -313,16 +314,16 @@ class Bucket(object): response = self.connection.make_request('GET', self.name, headers=headers, query_args=s) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 200: + if response.status_code == 200: rs = ResultSet(element_map) h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_all_keys(self, headers=None, **params): """ @@ -543,13 +544,13 @@ class Bucket(object): headers=hdrs, query_args=query_args, data=data) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: h = handler.XmlHandler(result, self) xml.sax.parseString(body, h) return count >= 1000 # more? else: - raise provider.storage_response_error(response.status, + raise provider.storage_response_error(response.status_code, response.reason, body) while delete_keys2(headers): @@ -588,9 +589,9 @@ class Bucket(object): response = self.connection.make_request('DELETE', self.name, key_name, headers=headers, query_args=query_args) - body = response.read() - if response.status != 204: - raise provider.storage_response_error(response.status, + body = response.content + if response.status_code != 204: + raise provider.storage_response_error(response.status_code, response.reason, body) def copy_key(self, new_key_name, src_bucket_name, @@ -683,8 +684,8 @@ class Bucket(object): response = self.connection.make_request('PUT', self.name, new_key_name, headers=headers, query_args=query_args) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: key = self.new_key(new_key_name) h = handler.XmlHandler(key, self) xml.sax.parseString(body, h) @@ -695,7 +696,7 @@ class Bucket(object): self.set_xml_acl(acl, new_key_name) return key else: - raise provider.storage_response_error(response.status, + raise provider.storage_response_error(response.status_code, response.reason, body) def set_canned_acl(self, acl_str, key_name='', headers=None, @@ -712,10 +713,10 @@ class Bucket(object): query_args += '&versionId=%s' % version_id response = self.connection.make_request('PUT', self.name, key_name, headers=headers, query_args=query_args) - body = response.read() - if response.status != 200: + body = response.content + if response.status_code != 200: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_xml_acl(self, key_name='', headers=None, version_id=None): query_args = 'acl' @@ -724,24 +725,26 @@ class Bucket(object): response = self.connection.make_request('GET', self.name, key_name, query_args=query_args, headers=headers) - body = response.read() - if response.status != 200: + body = response.content + if response.status_code != 200: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) return body def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None, query_args='acl'): if version_id: query_args += '&versionId=%s' % version_id + headers = headers or {} + headers['Content-Type'] = 'text/xml' response = self.connection.make_request('PUT', self.name, key_name, data=acl_str.encode('ISO-8859-1'), query_args=query_args, headers=headers) - body = response.read() - if response.status != 200: + body = response.content + if response.status_code != 200: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def set_acl(self, acl_or_str, key_name='', headers=None, version_id=None): if isinstance(acl_or_str, Policy): @@ -758,15 +761,15 @@ class Bucket(object): response = self.connection.make_request('GET', self.name, key_name, query_args=query_args, headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: policy = Policy(self) h = handler.XmlHandler(policy, self) xml.sax.parseString(body, h) return policy else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def set_subresource(self, subresource, value, key_name = '', headers=None, version_id=None): @@ -786,8 +789,8 @@ class Bucket(object): :type headers: dict :param headers: Additional HTTP headers to include in the request. - :type src_version_id: string - :param src_version_id: Optional. The version id of the key to operate + :type version_id: string + :param version_id: Optional. The version id of the key to operate on. If not specified, operate on the newest version. """ @@ -796,14 +799,16 @@ class Bucket(object): query_args = subresource if version_id: query_args += '&versionId=%s' % version_id + headers = headers or {} + headers['Content-Type'] = 'text/xml' response = self.connection.make_request('PUT', self.name, key_name, data=value.encode('UTF-8'), query_args=query_args, headers=headers) - body = response.read() - if response.status != 200: + body = response.content + if response.status_code != 200: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_subresource(self, subresource, key_name='', headers=None, version_id=None): @@ -820,8 +825,8 @@ class Bucket(object): :type headers: dict :param headers: Additional HTTP headers to include in the request. - :type src_version_id: string - :param src_version_id: Optional. The version id of the key to operate + :type version_id: string + :param version_id: Optional. The version id of the key to operate on. If not specified, operate on the newest version. @@ -836,10 +841,10 @@ class Bucket(object): response = self.connection.make_request('GET', self.name, key_name, query_args=query_args, headers=headers) - body = response.read() - if response.status != 200: + body = response.content + if response.status_code != 200: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) return body def make_public(self, recursive=False, headers=None): @@ -939,49 +944,53 @@ class Bucket(object): """ response = self.connection.make_request('GET', self.name, query_args='location') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: rs = ResultSet(self) h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs.LocationConstraint else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def enable_logging(self, target_bucket, target_prefix='', headers=None): if isinstance(target_bucket, Bucket): target_bucket = target_bucket.name body = self.BucketLoggingBody % (target_bucket, target_prefix) + headers = headers or {} + headers['Content-Type'] = 'text/xml' response = self.connection.make_request('PUT', self.name, data=body, query_args='logging', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def disable_logging(self, headers=None): body = self.EmptyBucketLoggingBody + headers = headers or {} + headers['Content-Type'] = 'text/xml' response = self.connection.make_request('PUT', self.name, data=body, query_args='logging', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_logging_status(self, headers=None): response = self.connection.make_request('GET', self.name, query_args='logging', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return body else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def set_as_logging_target(self, headers=None): policy = self.get_acl(headers=headers) @@ -994,23 +1003,25 @@ class Bucket(object): def get_request_payment(self, headers=None): response = self.connection.make_request('GET', self.name, query_args='requestPayment', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return body else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def set_request_payment(self, payer='BucketOwner', headers=None): body = self.BucketPaymentBody % payer + headers = headers or {} + headers['Content-Type'] = 'text/xml' response = self.connection.make_request('PUT', self.name, data=body, query_args='requestPayment', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def configure_versioning(self, versioning, mfa_delete=False, mfa_token=None, headers=None): @@ -1053,14 +1064,16 @@ class Bucket(object): headers = {} provider = self.connection.provider headers[provider.mfa_header] = ' '.join(mfa_token) + headers = headers or {} + headers['Content-Type'] = 'text/xml' response = self.connection.make_request('PUT', self.name, data=body, query_args='versioning', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_versioning_status(self, headers=None): """ @@ -1076,9 +1089,9 @@ class Bucket(object): """ response = self.connection.make_request('GET', self.name, query_args='versioning', headers=headers) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 200: + if response.status_code == 200: d = {} ver = re.search(self.VersionRE, body) if ver: @@ -1089,7 +1102,7 @@ class Bucket(object): return d else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def configure_lifecycle(self, lifecycle_config, headers=None): """ @@ -1109,12 +1122,12 @@ class Bucket(object): data=fp.getvalue(), query_args='lifecycle', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_lifecycle_config(self, headers=None): """ @@ -1126,16 +1139,16 @@ class Bucket(object): """ response = self.connection.make_request('GET', self.name, query_args='lifecycle', headers=headers) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 200: + if response.status_code == 200: lifecycle = Lifecycle(self) h = handler.XmlHandler(lifecycle, self) xml.sax.parseString(body, h) return lifecycle else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def delete_lifecycle_configuration(self, headers=None): """ @@ -1144,13 +1157,13 @@ class Bucket(object): response = self.connection.make_request('DELETE', self.name, query_args='lifecycle', headers=headers) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 204: + if response.status_code == 204: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def configure_website(self, suffix, error_key='', headers=None): """ @@ -1175,15 +1188,17 @@ class Bucket(object): else: error_frag = '' body = self.WebsiteBody % (suffix, error_frag) + headers = headers or {} + headers['Content-Type'] = 'text/xml' response = self.connection.make_request('PUT', self.name, data=body, query_args='website', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_website_configuration(self, headers=None): """ @@ -1205,16 +1220,16 @@ class Bucket(object): """ response = self.connection.make_request('GET', self.name, query_args='website', headers=headers) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 200: + if response.status_code == 200: e = boto.jsonresponse.Element() h = boto.jsonresponse.XmlHandler(e, None) h.parse(body) return e else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def delete_website_configuration(self, headers=None): """ @@ -1222,13 +1237,13 @@ class Bucket(object): """ response = self.connection.make_request('DELETE', self.name, query_args='website', headers=headers) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 204: + if response.status_code == 204: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def get_website_endpoint(self): """ @@ -1236,9 +1251,11 @@ class Bucket(object): bucket as a website. This doesn't validate whether the bucket has been correctly configured as a website or not. """ - l = [self.name] - l.append(S3WebsiteEndpointTranslate.translate_region(self.get_location())) - l.append('.'.join(self.connection.host.split('.')[-2:])) + l = [ + self.name, + S3WebsiteEndpointTranslate.translate_region(self.get_location()), + '.'.join(self.connection.host.split('.')[-2:]), + ] return '.'.join(l) def get_policy(self, headers=None): @@ -1248,12 +1265,12 @@ class Bucket(object): """ response = self.connection.make_request('GET', self.name, query_args='policy', headers=headers) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return body else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def set_policy(self, policy, headers=None): """ @@ -1262,28 +1279,30 @@ class Bucket(object): :type policy: str :param policy: The JSON policy as a string. """ + headers = headers or {} + headers['Content-Type'] = 'text/json' response = self.connection.make_request('PUT', self.name, data=policy, query_args='policy', headers=headers) - body = response.read() - if response.status >= 200 and response.status <= 204: + body = response.content + if response.status_code >= 200 and response.status_code <= 204: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def delete_policy(self, headers=None): response = self.connection.make_request('DELETE', self.name, data='/?policy', query_args='policy', headers=headers) - body = response.read() - if response.status >= 200 and response.status <= 204: + body = response.content + if response.status_code >= 200 and response.status_code <= 204: return True else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def initiate_multipart_upload(self, key_name, headers=None, @@ -1340,16 +1359,16 @@ class Bucket(object): response = self.connection.make_request('POST', self.name, key_name, query_args=query_args, headers=headers) - body = response.read() + body = response.content boto.log.debug(body) - if response.status == 200: + if response.status_code == 200: resp = MultiPartUpload(self) h = handler.XmlHandler(resp, self) xml.sax.parseString(body, h) return resp else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def complete_multipart_upload(self, key_name, upload_id, xml_body, headers=None): @@ -1364,32 +1383,32 @@ class Bucket(object): query_args=query_args, headers=headers, data=xml_body) contains_error = False - body = response.read() + body = response.content # Some errors will be reported in the body of the response # even though the HTTP response code is 200. This check # does a quick and dirty peek in the body for an error element. if body.find('<Error>') > 0: contains_error = True boto.log.debug(body) - if response.status == 200 and not contains_error: + if response.status_code == 200 and not contains_error: resp = CompleteMultiPartUpload(self) h = handler.XmlHandler(resp, self) xml.sax.parseString(body, h) return resp else: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def cancel_multipart_upload(self, key_name, upload_id, headers=None): query_args = 'uploadId=%s' % upload_id response = self.connection.make_request('DELETE', self.name, key_name, query_args=query_args, headers=headers) - body = response.read() + body = response.content boto.log.debug(body) - if response.status != 204: + if response.status_code != 204: raise self.connection.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def delete(self, headers=None): return self.connection.delete_bucket(self.name, headers=headers) diff --git a/boto/s3/connection.py b/boto/s3/connection.py index c757c137..dd878d13 100644 --- a/boto/s3/connection.py +++ b/boto/s3/connection.py @@ -339,10 +339,10 @@ class S3Connection(AWSAuthConnection): def get_all_buckets(self, headers=None): response = self.make_request('GET', headers=headers) - body = response.read() - if response.status > 300: + body = response.content + if response.status_code > 300: raise self.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) rs = ResultSet([('Bucket', self.bucket_class)]) h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) @@ -411,22 +411,22 @@ class S3Connection(AWSAuthConnection): location + '</LocationConstraint></CreateBucketConstraint>' response = self.make_request('PUT', bucket_name, headers=headers, data=data) - body = response.read() - if response.status == 409: + body = response.content + if response.status_code == 409: raise self.provider.storage_create_error( - response.status, response.reason, body) - if response.status == 200: + response.status_code, response.reason, body) + if response.status_code == 200: return self.bucket_class(self, bucket_name) else: raise self.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def delete_bucket(self, bucket, headers=None): response = self.make_request('DELETE', bucket, headers=headers) - body = response.read() - if response.status != 204: + body = response.content + if response.status_code != 204: raise self.provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) def make_request(self, method, bucket='', key='', headers=None, data='', query_args=None, sender=None, override_num_retries=None): diff --git a/boto/s3/key.py b/boto/s3/key.py index 206bcb6e..15e32281 100644 --- a/boto/s3/key.py +++ b/boto/s3/key.py @@ -109,12 +109,12 @@ class Key(object): base64md5 = base64.encodestring(digest) if base64md5[-1] == '\n': base64md5 = base64md5[0:-1] - return (md5_hexdigest, base64md5) + return md5_hexdigest, base64md5 def handle_encryption_headers(self, resp): provider = self.bucket.connection.provider if provider.server_side_encryption_header: - self.encrypted = resp.getheader(provider.server_side_encryption_header, None) + self.encrypted = resp.headers.get(provider.server_side_encryption_header, None) else: self.encrypted = None @@ -126,10 +126,10 @@ class Key(object): # version of the Key. In that case, we don't really want to # overwrite the version_id in this Key object. Comprende? if self.version_id is None or force: - self.version_id = resp.getheader(provider.version_id, None) - self.source_version_id = resp.getheader(provider.copy_source_version_id, + self.version_id = resp.headers.get(provider.version_id, None) + self.source_version_id = resp.headers.get(provider.copy_source_version_id, None) - if resp.getheader(provider.delete_marker, 'false') == 'true': + if resp.headers.get(provider.delete_marker, 'false') == 'true': self.delete_marker = True else: self.delete_marker = False @@ -155,7 +155,7 @@ class Key(object): the stored object in the response. See http://goo.gl/EWOPb for details. """ - if self.resp == None: + if self.resp is None: self.mode = 'r' provider = self.bucket.connection.provider @@ -163,14 +163,15 @@ class Key(object): 'GET', self.bucket.name, self.name, headers, query_args=query_args, override_num_retries=override_num_retries) - if self.resp.status < 199 or self.resp.status > 299: - body = self.resp.read() - raise provider.storage_response_error(self.resp.status, + + if self.resp.status_code < 199 or self.resp.status_code > 299: + body = self.resp.content + raise provider.storage_response_error(self.resp.status_code, self.resp.reason, body) - response_headers = self.resp.msg + response_headers = self.resp.headers self.metadata = boto.utils.get_aws_metadata(response_headers, provider) - for name,value in response_headers.items(): + for name, value in response_headers.items(): # To get correct size for Range GETs, use Content-Range # header if one was returned. If not, use Content-Length # header. @@ -222,8 +223,7 @@ class Key(object): closed = False def close(self): - if self.resp: - self.resp.read() + # TODO: This may not be necessary anymore with requests integrated. self.resp = None self.mode = None self.closed = True @@ -239,18 +239,29 @@ class Key(object): All of the HTTP connection stuff is handled for you. """ self.open_read() - data = self.resp.read(self.BufferSize) + data = self.resp.iter_content(chunk_size=self.BufferSize) if not data: self.close() raise StopIteration return data def read(self, size=0): + """ + Reads either the entire contents of the key at once (by default), + or returns an iterator with the given chunk size. + + :keyword int size: Chunk size for the iterator, if non-zero. + + :rtype: bytes or iterator of bytes + :returns: If no ``size`` is specified, returns the entire contents + of the key. If ``size`` is specified, returns an iterator with + the given chunk size. + """ self.open_read() - if size == 0: - data = self.resp.read() + if size is 0: + data = self.resp.content else: - data = self.resp.read(size) + data = self.resp.iter_content(chunk_size=size) if not data: self.close() return data @@ -501,6 +512,7 @@ class Key(object): spos = None self.read_from_stream = False + # TODO: Replace this with requests implementation, once it exists. def sender(http_conn, method, path, data, headers): # This function is called repeatedly for temporary retries # so we must be sure the file pointer is pointing at the @@ -601,22 +613,22 @@ class Key(object): cb(data_len, cb_size) response = http_conn.getresponse() - body = response.read() + body = response.content http_conn.set_debuglevel(save_debug) self.bucket.connection.debug = save_debug - if ((response.status == 500 or response.status == 503 or - response.getheader('location')) and not chunked_transfer): + if ((response.status_code == 500 or response.status_code == 503 or + response.headers.get('location')) and not chunked_transfer): # we'll try again. return response - elif response.status >= 200 and response.status <= 299: - self.etag = response.getheader('etag') + elif response.status_code >= 200 and response.status_code <= 299: + self.etag = response.headers.get('etag') if self.etag != '"%s"' % self.md5: raise provider.storage_data_error( 'ETag from S3 did not match computed MD5') return response else: raise provider.storage_response_error( - response.status, response.reason, body) + response.status_code, response.reason, body) if not headers: headers = {} @@ -639,7 +651,7 @@ class Key(object): del headers['Content-Type'] elif self.path: self.content_type = mimetypes.guess_type(self.path)[0] - if self.content_type == None: + if self.content_type is None: self.content_type = self.DefaultContentType headers['Content-Type'] = self.content_type else: @@ -768,7 +780,7 @@ class Key(object): if provider.storage_class_header: headers[provider.storage_class_header] = self.storage_class - if self.bucket != None: + if self.bucket is not None: if not replace: if self.bucket.lookup(self.name): return @@ -1046,7 +1058,7 @@ class Key(object): :type fp: file :param fp: File pointer to put the data into - :type headers: string + :type headers: dict :param: headers to send when retrieving the files :type cb: function @@ -1077,6 +1089,16 @@ class Key(object): the stored object in the response. See http://goo.gl/EWOPb for details. """ + if cb: + if num_cb > 2: + cb_count = self.size / self.BufferSize / (num_cb - 2) + elif num_cb < 0: + cb_count = -1 + else: + cb_count = 0 + i = total_bytes = 0 + cb(total_bytes, self.size) + save_debug = self.bucket.connection.debug if self.bucket.connection.debug == 1: self.bucket.connection.debug = 0 @@ -1093,33 +1115,17 @@ class Key(object): if version_id is None: version_id = self.version_id if version_id: + # TODO: Do we need to be assembling strings anymore with requests? query_args.append('versionId=%s' % version_id) if response_headers: + # TODO: Is this necessary with requests? for key in response_headers: query_args.append('%s=%s' % (key, urllib.quote(response_headers[key]))) query_args = '&'.join(query_args) self.open('r', headers, query_args=query_args, override_num_retries=override_num_retries) - data_len = 0 - if cb: - if self.size is None: - cb_size = 0 - else: - cb_size = self.size - if self.size is None and num_cb != -1: - # If size is not available due to chunked transfer for example, - # we'll call the cb for every 1MB of data transferred. - cb_count = (1024 * 1024)/self.BufferSize - elif num_cb > 1: - cb_count = int(math.ceil(cb_size/self.BufferSize/(num_cb-1.0))) - elif num_cb < 0: - cb_count = -1 - else: - cb_count = 0 - i = 0 - cb(data_len, cb_size) - for bytes in self: + for bytes in self.read(size=self.BufferSize): fp.write(bytes) data_len += len(bytes) if m: @@ -1205,7 +1211,7 @@ class Key(object): :param torrent: If True, returns the contents of a torrent file as a string. - :type res_upload_handler: ResumableDownloadHandler + :type res_download_handler: ResumableDownloadHandler :param res_download_handler: If provided, this handler will perform the download. @@ -1215,7 +1221,7 @@ class Key(object): the stored object in the response. See http://goo.gl/EWOPb for details. """ - if self.bucket != None: + if self.bucket is not None: if res_download_handler: res_download_handler.get_file(self, fp, headers, cb, num_cb, torrent=torrent, @@ -1262,7 +1268,7 @@ class Key(object): :param torrent: If True, returns the contents of a torrent file as a string. - :type res_upload_handler: ResumableDownloadHandler + :type res_download_handler: ResumableDownloadHandler :param res_download_handler: If provided, this handler will perform the download. @@ -1279,7 +1285,7 @@ class Key(object): response_headers=response_headers) fp.close() # if last_modified date was sent from s3, try to set file's timestamp - if self.last_modified != None: + if self.last_modified is not None: try: modified_tuple = rfc822.parsedate_tz(self.last_modified) modified_stamp = int(rfc822.mktime_tz(modified_tuple)) @@ -1348,15 +1354,6 @@ class Key(object): :type email_address: string :param email_address: The email address associated with the AWS account your are granting the permission to. - - :type recursive: boolean - :param recursive: A boolean value to controls whether the command - will apply the grant to all keys within the bucket - or not. The default value is False. By passing a - True value, the call will iterate through all keys - in the bucket and apply the same grant to each key. - CAUTION: If you have a lot of keys, this could take - a long time! """ policy = self.get_acl(headers=headers) policy.acl.add_email_grant(permission, email_address) diff --git a/boto/s3/multipart.py b/boto/s3/multipart.py index 4eddc397..dde83464 100644 --- a/boto/s3/multipart.py +++ b/boto/s3/multipart.py @@ -204,8 +204,8 @@ class MultiPartUpload(object): response = self.bucket.connection.make_request('GET', self.bucket.name, self.key_name, query_args=query_args) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: h = handler.XmlHandler(self, self) xml.sax.parseString(body, h) return self._parts diff --git a/boto/s3/resumable_download_handler.py b/boto/s3/resumable_download_handler.py index ffa20957..9efb0f28 100644 --- a/boto/s3/resumable_download_handler.py +++ b/boto/s3/resumable_download_handler.py @@ -222,7 +222,7 @@ class ResumableDownloadHandler(object): :type fp: file :param fp: File pointer into which data should be downloaded - :type headers: string + :type headers: dict :param: headers to send when retrieving the files :type cb: function diff --git a/boto/sdb/connection.py b/boto/sdb/connection.py index f0431932..44299a2e 100644 --- a/boto/sdb/connection.py +++ b/boto/sdb/connection.py @@ -491,15 +491,15 @@ class SDBConnection(AWSQueryConnection): attribute_names = [attribute_names] self.build_list_params(params, attribute_names, 'AttributeName') response = self.make_request('GetAttributes', params) - body = response.read() - if response.status == 200: - if item == None: + body = response.content + if response.status_code == 200: + if item is None: item = self.item_cls(domain, item_name) h = handler.XmlHandler(item, self) xml.sax.parseString(body, h) return item else: - raise SDBResponseError(response.status, response.reason, body) + raise SDBResponseError(response.status_code, response.reason, body) def delete_attributes(self, domain_or_name, item_name, attr_names=None, expected_value=None): diff --git a/boto/ses/connection.py b/boto/ses/connection.py index c9ae41d4..6d479c45 100644 --- a/boto/ses/connection.py +++ b/boto/ses/connection.py @@ -97,8 +97,8 @@ class SESConnection(AWSAuthConnection): headers=headers, data=urllib.urlencode(params) ) - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: list_markers = ('VerifiedEmailAddresses', 'SendDataPoints') e = boto.jsonresponse.Element(list_marker=list_markers) h = boto.jsonresponse.XmlHandler(e, None) @@ -115,7 +115,7 @@ class SESConnection(AWSAuthConnection): errors share the same HTTP response code, meaning we have to get really kludgey and do string searches to figure out what went wrong. """ - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) if "Address blacklisted." in body: @@ -149,7 +149,7 @@ class SESConnection(AWSAuthConnection): ExceptionToRaise = self.ResponseError exc_reason = response.reason - raise ExceptionToRaise(response.status, exc_reason, body) + raise ExceptionToRaise(response.status_code, exc_reason, body) def send_email(self, source, subject, body, to_addresses, cc_addresses=None, bcc_addresses=None, format='text', reply_addresses=None, diff --git a/boto/sns/connection.py b/boto/sns/connection.py index 6ce4ff19..2bc1ad0b 100644 --- a/boto/sns/connection.py +++ b/boto/sns/connection.py @@ -66,13 +66,13 @@ class SNSConnection(AWSQueryConnection): if next_token: params['NextToken'] = next_token response = self.make_request('ListTopics', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def get_topic_attributes(self, topic): """ @@ -85,13 +85,13 @@ class SNSConnection(AWSQueryConnection): params = {'ContentType' : 'JSON', 'TopicArn' : topic} response = self.make_request('GetTopicAttributes', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def set_topic_attributes(self, topic, attr_name, attr_value): """ @@ -114,13 +114,13 @@ class SNSConnection(AWSQueryConnection): 'AttributeName' : attr_name, 'AttributeValue' : attr_value} response = self.make_request('SetTopicAttributes', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def add_permission(self, topic, label, account_ids, actions): """ @@ -148,13 +148,13 @@ class SNSConnection(AWSQueryConnection): self.build_list_params(params, account_ids, 'AWSAccountId') self.build_list_params(params, actions, 'ActionName') response = self.make_request('AddPermission', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def remove_permission(self, topic, label): """ @@ -172,13 +172,13 @@ class SNSConnection(AWSQueryConnection): 'TopicArn' : topic, 'Label' : label} response = self.make_request('RemovePermission', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def create_topic(self, topic): """ @@ -191,13 +191,13 @@ class SNSConnection(AWSQueryConnection): params = {'ContentType' : 'JSON', 'Name' : topic} response = self.make_request('CreateTopic', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def delete_topic(self, topic): """ @@ -210,13 +210,13 @@ class SNSConnection(AWSQueryConnection): params = {'ContentType' : 'JSON', 'TopicArn' : topic} response = self.make_request('DeleteTopic', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) @@ -243,13 +243,13 @@ class SNSConnection(AWSQueryConnection): if subject: params['Subject'] = subject response = self.make_request('Publish', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def subscribe(self, topic, protocol, endpoint): """ @@ -278,13 +278,13 @@ class SNSConnection(AWSQueryConnection): 'Protocol' : protocol, 'Endpoint' : endpoint} response = self.make_request('Subscribe', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def subscribe_sqs_queue(self, topic, queue): """ @@ -353,13 +353,13 @@ class SNSConnection(AWSQueryConnection): if authenticate_on_unsubscribe: params['AuthenticateOnUnsubscribe'] = 'true' response = self.make_request('ConfirmSubscription', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def unsubscribe(self, subscription): """ @@ -373,13 +373,13 @@ class SNSConnection(AWSQueryConnection): params = {'ContentType' : 'JSON', 'SubscriptionArn' : subscription} response = self.make_request('Unsubscribe', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def get_all_subscriptions(self, next_token=None): """ @@ -394,13 +394,13 @@ class SNSConnection(AWSQueryConnection): if next_token: params['NextToken'] = next_token response = self.make_request('ListSubscriptions', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) def get_all_subscriptions_by_topic(self, topic, next_token=None): """ @@ -421,11 +421,11 @@ class SNSConnection(AWSQueryConnection): params['NextToken'] = next_token response = self.make_request('ListSubscriptionsByTopic', params, '/', 'GET') - body = response.read() - if response.status == 200: + body = response.content + if response.status_code == 200: return json.loads(body) else: - boto.log.error('%s %s' % (response.status, response.reason)) + boto.log.error('%s %s' % (response.status_code, response.reason)) boto.log.error('%s' % body) - raise self.ResponseError(response.status, response.reason, body) + raise self.ResponseError(response.status_code, response.reason, body) diff --git a/boto/utils.py b/boto/utils.py index 2eeb3d32..4817dd33 100644 --- a/boto/utils.py +++ b/boto/utils.py @@ -161,8 +161,10 @@ def get_aws_metadata(headers, provider=None): metadata = {} for hkey in headers.keys(): if hkey.lower().startswith(metadata_prefix): + # TODO: Is this necessary anymore with requests? val = urllib.unquote_plus(headers[hkey]) try: + # TODO: Is this necessary anymore with requests? metadata[hkey[len(metadata_prefix):]] = unicode(val, 'utf-8') except UnicodeDecodeError: metadata[hkey[len(metadata_prefix):]] = val diff --git a/tests/s3/test_bucket.py b/tests/s3/test_bucket.py index ad2e6cac..71a093d5 100644 --- a/tests/s3/test_bucket.py +++ b/tests/s3/test_bucket.py @@ -51,9 +51,8 @@ class S3BucketTest (unittest.TestCase): # last element will be "b" so no issue. rs = self.bucket.get_all_keys(max_keys=2) for element in rs: - pass - self.assertEqual(element.name, "b") - self.assertEqual(rs.next_marker, None) + self.assertEqual(element.name, "b") + self.assertEqual(rs.next_marker, None) # list using delimiter of first 2 keys will have # a NextMarker set (when truncated). As prefixes @@ -61,13 +60,12 @@ class S3BucketTest (unittest.TestCase): # last element, but luckily we have next_marker. rs = self.bucket.get_all_keys(max_keys=2, delimiter="/") for element in rs: - pass - self.assertEqual(element.name, "a/") - self.assertEqual(rs.next_marker, "b") + self.assertEqual(element.name, "a/") + self.assertEqual(rs.next_marker, "b") # ensure bucket.list() still works by just # popping elements off the front of expected. rs = self.bucket.list() for element in rs: self.assertEqual(element.name, expected.pop(0)) - self.assertEqual(expected, []) + self.assertEqual(expected, []) |