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

Unified Diff: boto/connection.py

Issue 8386013: Merging in latest boto. (Closed) Base URL: svn://svn.chromium.org/boto
Patch Set: Redoing vendor drop by deleting and then merging. Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « boto/cloudfront/invalidation.py ('k') | boto/ec2/__init__.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: boto/connection.py
diff --git a/boto/connection.py b/boto/connection.py
index 76e9ffeadd9b80d554e4a05382cc47f404be4dd5..3c9f2375e4a799aedeb8eba49416eb7550ceffa7 100644
--- a/boto/connection.py
+++ b/boto/connection.py
@@ -3,6 +3,7 @@
# Copyright (c) 2008 rPath, Inc.
# Copyright (c) 2009 The Echo Nest Corporation
# Copyright (c) 2010, Eucalyptus Systems, Inc.
+# Copyright (c) 2011, Nexenta Systems Inc.
# All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
@@ -41,11 +42,13 @@
Handles basic connections to AWS
"""
+from __future__ import with_statement
import base64
import errno
import httplib
import os
import Queue
+import random
import re
import socket
import sys
@@ -57,27 +60,227 @@ import auth
import auth_handler
import boto
import boto.utils
+import boto.handler
+import boto.cacerts
-from boto import config, UserAgent, handler
+from boto import config, UserAgent
from boto.exception import AWSConnectionError, BotoClientError, BotoServerError
from boto.provider import Provider
from boto.resultset import ResultSet
+HAVE_HTTPS_CONNECTION = False
+try:
+ import ssl
+ from boto import https_connection
+ # Google App Engine runs on Python 2.5 so doesn't have ssl.SSLError.
+ if hasattr(ssl, 'SSLError'):
+ HAVE_HTTPS_CONNECTION = True
+except ImportError:
+ pass
+
+try:
+ import threading
+except ImportError:
+ import dummy_threading as threading
+
+ON_APP_ENGINE = all(key in os.environ for key in (
+ 'USER_IS_ADMIN', 'CURRENT_VERSION_ID', 'APPLICATION_ID'))
PORTS_BY_SECURITY = { True: 443, False: 80 }
-class ConnectionPool:
- def __init__(self, hosts, connections_per_host):
- self._hosts = boto.utils.LRUCache(hosts)
- self.connections_per_host = connections_per_host
+DEFAULT_CA_CERTS_FILE = os.path.join(
+ os.path.dirname(os.path.abspath(boto.cacerts.__file__ )), "cacerts.txt")
- def __getitem__(self, key):
- if key not in self._hosts:
- self._hosts[key] = Queue.Queue(self.connections_per_host)
- return self._hosts[key]
+class HostConnectionPool(object):
- def __repr__(self):
- return 'ConnectionPool:%s' % ','.join(self._hosts._dict.keys())
+ """
+ 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 = conn._HTTPConnection__response
+ 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):
+
+ """
+ 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.
+ """
+
+ #
+ # 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.
+ """
+ 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):
@@ -89,7 +292,7 @@ class HTTPRequest(object):
:param method: The HTTP method name, 'GET', 'POST', 'PUT' etc.
:type protocol: string
- :param protocol: The http protocol used, 'http' or 'https'.
+ :param protocol: The http protocol used, 'http' or 'https'.
:type host: string
:param host: Host to which the request is addressed. eg. abc.com
@@ -98,10 +301,10 @@ class HTTPRequest(object):
:param port: port on which the request is being sent. Zero means unset,
in which case default port will be chosen.
- :type path: string
+ :type path: string
:param path: URL path that is bein accessed.
- :type auth_path: string
+ :type auth_path: string
:param path: The part of the URL path used when creating the
authentication string.
@@ -119,12 +322,21 @@ class HTTPRequest(object):
"""
self.method = method
self.protocol = protocol
- self.host = host
+ self.host = host
self.port = port
self.path = path
+ if auth_path is None:
+ auth_path = path
self.auth_path = auth_path
self.params = params
- self.headers = headers
+ # 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':
+ self.headers = headers.copy()
+ del self.headers['Transfer-Encoding']
+ else:
+ self.headers = headers
self.body = body
def __str__(self):
@@ -133,20 +345,37 @@ class HTTPRequest(object):
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
+ # I'm not sure if this is still needed, now that add_auth is
+ # setting the content-length for POST requests.
+ if not self.headers.has_key('Content-Length'):
+ if not self.headers.has_key('Transfer-Encoding') or \
+ self.headers['Transfer-Encoding'] != 'chunked':
+ self.headers['Content-Length'] = str(len(self.body))
+
class AWSAuthConnection(object):
def __init__(self, host, aws_access_key_id=None, aws_secret_access_key=None,
is_secure=True, port=None, proxy=None, proxy_port=None,
proxy_user=None, proxy_pass=None, debug=0,
- https_connection_factory=None, path='/', provider='aws'):
+ https_connection_factory=None, path='/',
+ provider='aws', security_token=None):
"""
:type host: str
:param host: The host to make the connection to
-
+
:keyword str aws_access_key_id: Your AWS Access Key ID (provided by
- Amazon). If none is specified, the value in your
+ Amazon). If none is specified, the value in your
``AWS_ACCESS_KEY_ID`` environmental variable is used.
- :keyword str aws_secret_access_key: Your AWS Secret Access Key
- (provided by Amazon). If none is specified, the value in your
+ :keyword str aws_secret_access_key: Your AWS Secret Access Key
+ (provided by Amazon). If none is specified, the value in your
``AWS_SECRET_ACCESS_KEY`` environmental variable is used.
:type is_secure: boolean
@@ -172,15 +401,35 @@ class AWSAuthConnection(object):
:type port: int
:param port: The port to use to connect
"""
- self.num_retries = 5
+ self.num_retries = 6
# Override passed-in is_secure setting if value was defined in config.
if config.has_option('Boto', 'is_secure'):
is_secure = config.getboolean('Boto', 'is_secure')
self.is_secure = is_secure
+ # Whether or not to validate server certificates. At some point in the
+ # future, the default should be flipped to true.
+ self.https_validate_certificates = config.getbool(
+ 'Boto', 'https_validate_certificates', False)
+ if self.https_validate_certificates and not HAVE_HTTPS_CONNECTION:
+ raise BotoClientError(
+ "SSL server certificate validation is enabled in boto "
+ "configuration, but Python dependencies required to "
+ "support this feature are not available. Certificate "
+ "validation is only supported when running under Python "
+ "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)
+ # define subclasses of the above that are not retryable.
+ self.http_unretryable_exceptions = []
+ if HAVE_HTTPS_CONNECTION:
+ self.http_unretryable_exceptions.append(ssl.SSLError)
+ self.http_unretryable_exceptions.append(
+ https_connection.InvalidCertificateException)
+
# define values in socket exceptions we don't want to catch
self.socket_exception_values = (errno.EINTR,)
if https_connection_factory is not None:
@@ -203,20 +452,31 @@ 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
+
self.provider = Provider(provider,
aws_access_key_id,
- aws_secret_access_key)
+ aws_secret_access_key,
+ security_token)
# allow config file to override default host
if self.provider.host:
self.host = self.provider.host
- # cache up to 20 connections per host, up to 20 hosts
- self._pool = ConnectionPool(20, 20)
+ self._pool = ConnectionPool()
self._connection = (self.server_name(), self.is_secure)
self._last_rs = None
self._auth_handler = auth.get_auth_handler(
- host, config, self.provider, self._required_auth_capability())
+ host, config, self.provider, self._required_auth_capability())
def __repr__(self):
return '%s:%s' % (self.__class__.__name__, self.host)
@@ -224,13 +484,6 @@ class AWSAuthConnection(object):
def _required_auth_capability(self):
return []
- def _cached_name(self, host, is_secure):
- if host is None:
- host = self.server_name()
- cached_name = is_secure and 'https://' or 'http://'
- cached_name += host
- return cached_name
-
def connection(self):
return self.get_http_connection(*self._connection)
connection = property(connection)
@@ -281,7 +534,8 @@ class AWSAuthConnection(object):
# did the same when calculating the V2 signature. In 2.6
# (and higher!)
# it no longer does that. Hence, this kludge.
- if sys.version[:3] in ('2.6', '2.7') and port == 443:
+ if ((ON_APP_ENGINE and sys.version[:3] == '2.5') or
+ sys.version[:3] in ('2.6', '2.7')) and port == 443:
signature_host = self.host
else:
signature_host = '%s:%d' % (self.host, port)
@@ -322,10 +576,10 @@ class AWSAuthConnection(object):
self.use_proxy = (self.proxy != None)
def get_http_connection(self, host, is_secure):
- queue = self._pool[self._cached_name(host, is_secure)]
- try:
- return queue.get_nowait()
- except Queue.Empty:
+ 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):
@@ -334,16 +588,25 @@ class AWSAuthConnection(object):
if host is None:
host = self.server_name()
if is_secure:
- boto.log.debug('establishing HTTPS connection')
+ 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)
+ connection = httplib.HTTPSConnection(host,
+ **self.http_connection_kwargs)
else:
- boto.log.debug('establishing HTTP connection')
- connection = httplib.HTTPConnection(host)
+ 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
@@ -354,11 +617,7 @@ class AWSAuthConnection(object):
return connection
def put_http_connection(self, host, is_secure, connection):
- try:
- self._pool[self._cached_name(host, is_secure)].put_nowait(connection)
- except Queue.Full:
- # gracefully fail in case of pool overflow
- connection.close()
+ self._pool.put_http_connection(host, is_secure, connection)
def proxy_ssl(self):
host = '%s:%d' % (self.host, self.port)
@@ -367,13 +626,14 @@ class AWSAuthConnection(object):
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)
+ resp = httplib.HTTPResponse(sock, strict=True, debuglevel=self.debug)
resp.begin()
if resp.status != 200:
@@ -388,12 +648,29 @@ class AWSAuthConnection(object):
h = httplib.HTTPConnection(host)
- # Wrap the socket in an SSL socket
- if hasattr(httplib, 'ssl'):
- sslSock = httplib.ssl.SSLSocket(sock)
- else: # Old Python, no ssl module
- sslSock = socket.ssl(sock, None, None)
- sslSock = httplib.FakeSocket(sock, sslSock)
+ 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
@@ -406,8 +683,7 @@ class AWSAuthConnection(object):
auth = base64.encodestring(self.proxy_user + ':' + self.proxy_pass)
return {'Proxy-Authorization': 'Basic %s' % auth}
- def _mexe(self, method, path, data, headers, host=None, sender=None,
- override_num_retries=None):
+ def _mexe(self, request, sender=None, override_num_retries=None):
"""
mexe - Multi-execute inside a loop, retrying multiple times to handle
transient Internet errors by simply trying again.
@@ -416,11 +692,11 @@ class AWSAuthConnection(object):
This code was inspired by the S3Utils classes posted to the boto-users
Google group by Larry Bates. Thanks!
"""
- boto.log.debug('Method: %s' % method)
- boto.log.debug('Path: %s' % path)
- boto.log.debug('Data: %s' % data)
- boto.log.debug('Headers: %s' % headers)
- boto.log.debug('Host: %s' % host)
+ 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
@@ -429,49 +705,53 @@ class AWSAuthConnection(object):
else:
num_retries = override_num_retries
i = 0
- connection = self.get_http_connection(host, self.is_secure)
+ connection = self.get_http_connection(request.host, self.is_secure)
while i <= num_retries:
+ # Use binary exponential backoff to desynchronize client requests
+ next_sleep = random.random() * (2 ** i)
try:
+ # we now re-sign each request before it is retried
+ request.authorize(connection=self)
if callable(sender):
- response = sender(connection, method, path, data, headers)
+ response = sender(connection, request.method, request.path,
+ request.body, request.headers)
else:
- connection.request(method, path, data, headers)
+ 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 method == 'HEAD' and getattr(response, 'chunked', False):
+ if request.method == 'HEAD' and getattr(response, 'chunked', False):
response.chunked = 0
if response.status == 500 or response.status == 503:
- boto.log.debug('received %d response, retrying in %d seconds' % (response.status, 2 ** i))
+ boto.log.debug('received %d response, retrying in %3.1f seconds' %
+ (response.status, next_sleep))
body = response.read()
- elif response.status == 408:
- body = response.read()
- print '-------------------------'
- print ' 4 0 8 '
- print 'path=%s' % path
- print body
- print '-------------------------'
elif response.status < 300 or response.status >= 400 or \
not location:
- self.put_http_connection(host, self.is_secure, connection)
+ self.put_http_connection(request.host, self.is_secure, connection)
return response
else:
- scheme, host, path, params, query, fragment = \
+ scheme, request.host, request.path, params, query, fragment = \
urlparse.urlparse(location)
if query:
- path += '?' + query
- boto.log.debug('Redirecting: %s' % scheme + '://' + host + path)
- connection = self.get_http_connection(host, scheme == 'https')
+ request.path += '?' + query
+ boto.log.debug('Redirecting: %s' % scheme + '://' + request.host + request.path)
+ connection = self.get_http_connection(request.host, scheme == 'https')
continue
- except KeyboardInterrupt:
- sys.exit('Keyboard Interrupt')
except self.http_exceptions, e:
+ for unretryable in self.http_unretryable_exceptions:
+ if isinstance(e, unretryable):
+ boto.log.debug(
+ 'encountered unretryable %s exception, re-raising' %
+ e.__class__.__name__)
+ raise e
boto.log.debug('encountered %s exception, reconnecting' % \
e.__class__.__name__)
- connection = self.new_http_connection(host, self.is_secure)
- time.sleep(2 ** i)
+ 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 and stil haven't
# succeeded. So, if we have a response object, use it to raise an exception.
@@ -498,6 +778,8 @@ class AWSAuthConnection(object):
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
@@ -506,34 +788,12 @@ class AWSAuthConnection(object):
return HTTPRequest(method, self.protocol, host, self.port,
path, auth_path, params, headers, data)
- def fill_in_auth(self, http_request, **kwargs):
- headers = http_request.headers
- for key in headers:
- val = headers[key]
- if isinstance(val, unicode):
- headers[key] = urllib.quote_plus(val.encode('utf-8'))
-
- self._auth_handler.add_auth(http_request, **kwargs)
-
- headers['User-Agent'] = UserAgent
- if not headers.has_key('Content-Length'):
- headers['Content-Length'] = str(len(http_request.body))
- return http_request
-
- def _send_http_request(self, http_request, sender=None,
- override_num_retries=None):
- return self._mexe(http_request.method, http_request.path,
- http_request.body, http_request.headers,
- http_request.host, sender, override_num_retries)
-
def make_request(self, method, path, headers=None, data='', host=None,
auth_path=None, sender=None, override_num_retries=None):
"""Makes a request to the server, with stock multiple-retry logic."""
http_request = self.build_base_http_request(method, path, auth_path,
{}, headers, data, host)
- http_request = self.fill_in_auth(http_request)
- return self._send_http_request(http_request, sender,
- override_num_retries)
+ return self._mexe(http_request, sender, override_num_retries)
def close(self):
"""(Optional) Close any open HTTP connections. This is non-destructive,
@@ -550,10 +810,13 @@ class AWSQueryConnection(AWSAuthConnection):
def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
is_secure=True, port=None, proxy=None, proxy_port=None,
proxy_user=None, proxy_pass=None, host=None, debug=0,
- https_connection_factory=None, path='/'):
- AWSAuthConnection.__init__(self, host, aws_access_key_id, aws_secret_access_key,
- is_secure, port, proxy, proxy_port, proxy_user, proxy_pass,
- debug, https_connection_factory, path)
+ https_connection_factory=None, path='/', security_token=None):
+ AWSAuthConnection.__init__(self, host, aws_access_key_id,
+ aws_secret_access_key,
+ is_secure, port, proxy,
+ proxy_port, proxy_user, proxy_pass,
+ debug, https_connection_factory, path,
+ security_token=security_token)
def _required_auth_capability(self):
return []
@@ -568,8 +831,7 @@ class AWSQueryConnection(AWSAuthConnection):
if action:
http_request.params['Action'] = action
http_request.params['Version'] = self.APIVersion
- http_request = self.fill_in_auth(http_request)
- return self._send_http_request(http_request)
+ return self._mexe(http_request)
def build_list_params(self, params, items, label):
if isinstance(items, str):
@@ -579,7 +841,8 @@ class AWSQueryConnection(AWSAuthConnection):
# generics
- def get_list(self, action, params, markers, path='/', parent=None, verb='GET'):
+ def get_list(self, action, params, markers, path='/',
+ parent=None, verb='GET'):
if not parent:
parent = self
response = self.make_request(action, params, path, verb)
@@ -590,7 +853,7 @@ class AWSQueryConnection(AWSAuthConnection):
raise self.ResponseError(response.status, response.reason, body)
elif response.status == 200:
rs = ResultSet(markers)
- h = handler.XmlHandler(rs, parent)
+ h = boto.handler.XmlHandler(rs, parent)
xml.sax.parseString(body, h)
return rs
else:
@@ -598,7 +861,8 @@ class AWSQueryConnection(AWSAuthConnection):
boto.log.error('%s' % body)
raise self.ResponseError(response.status, response.reason, body)
- def get_object(self, action, params, cls, path='/', parent=None, verb='GET'):
+ 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)
@@ -609,7 +873,7 @@ class AWSQueryConnection(AWSAuthConnection):
raise self.ResponseError(response.status, response.reason, body)
elif response.status == 200:
obj = cls(parent)
- h = handler.XmlHandler(obj, parent)
+ h = boto.handler.XmlHandler(obj, parent)
xml.sax.parseString(body, h)
return obj
else:
@@ -628,7 +892,7 @@ class AWSQueryConnection(AWSAuthConnection):
raise self.ResponseError(response.status, response.reason, body)
elif response.status == 200:
rs = ResultSet()
- h = handler.XmlHandler(rs, parent)
+ h = boto.handler.XmlHandler(rs, parent)
xml.sax.parseString(body, h)
return rs.status
else:
« no previous file with comments | « boto/cloudfront/invalidation.py ('k') | boto/ec2/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698