summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/swift8
-rw-r--r--swiftclient/client.py31
-rw-r--r--swiftclient/https_connection.py95
-rw-r--r--tests/test_swiftclient.py8
4 files changed, 133 insertions, 9 deletions
diff --git a/bin/swift b/bin/swift
index e30d697..7fdaf39 100755
--- a/bin/swift
+++ b/bin/swift
@@ -50,7 +50,8 @@ def get_conn(options):
os_options=options.os_options,
snet=options.snet,
cacert=options.os_cacert,
- insecure=options.insecure)
+ insecure=options.insecure,
+ ssl_compression=options.ssl_compression)
def mkdirs(path):
@@ -1268,6 +1269,11 @@ Examples:
'be verified. '
'Defaults to env[SWIFTCLIENT_INSECURE] '
'(set to \'true\' to enable).')
+ parser.add_option('--no-ssl-compression',
+ action='store_false', dest='ssl_compression',
+ default=True,
+ help='Disable SSL compression when using https. '
+ 'This may increase performance.')
parser.disable_interspersed_args()
(options, args) = parse_args(parser, argv[1:], enforce_requires=False)
parser.enable_interspersed_args()
diff --git a/swiftclient/client.py b/swiftclient/client.py
index afd85f0..cb09a37 100644
--- a/swiftclient/client.py
+++ b/swiftclient/client.py
@@ -28,6 +28,10 @@ from urlparse import urlparse, urlunparse
from httplib import HTTPException, HTTPConnection, HTTPSConnection
from time import sleep
+try:
+ from swiftclient.https_connection import HTTPSConnectionNoSSLComp
+except ImportError:
+ HTTPSConnectionNoSSLComp = HTTPSConnection
logger = logging.getLogger("swiftclient")
@@ -141,23 +145,32 @@ class ClientException(Exception):
return b and '%s: %s' % (a, b) or a
-def http_connection(url, proxy=None):
+def http_connection(url, proxy=None, ssl_compression=True):
"""
Make an HTTPConnection or HTTPSConnection
:param url: url to connect to
:param proxy: proxy to connect through, if any; None by default; str of the
format 'http://127.0.0.1:8888' to set one
+ :param ssl_compression: Whether to enable compression at the SSL layer.
+ If set to 'False' and the pyOpenSSL library is
+ present an attempt to disable SSL compression
+ will be made. This may provide a performance
+ increase for https upload/download operations.
:returns: tuple of (parsed url, connection object)
:raises ClientException: Unable to handle protocol scheme
"""
url = encode_utf8(url)
parsed = urlparse(url)
proxy_parsed = urlparse(proxy) if proxy else None
+ host = proxy_parsed if proxy else parsed.netloc
if parsed.scheme == 'http':
- conn = HTTPConnection((proxy_parsed if proxy else parsed).netloc)
+ conn = HTTPConnection(host)
elif parsed.scheme == 'https':
- conn = HTTPSConnection((proxy_parsed if proxy else parsed).netloc)
+ if ssl_compression is True:
+ conn = HTTPSConnection(host)
+ else:
+ conn = HTTPSConnectionNoSSLComp(host)
else:
raise ClientException('Cannot handle protocol scheme %s for url %s' %
(parsed.scheme, repr(url)))
@@ -956,7 +969,8 @@ class Connection(object):
def __init__(self, authurl=None, user=None, key=None, retries=5,
preauthurl=None, preauthtoken=None, snet=False,
starting_backoff=1, tenant_name=None, os_options=None,
- auth_version="1", cacert=None, insecure=False):
+ auth_version="1", cacert=None, insecure=False,
+ ssl_compression=True):
"""
:param authurl: authentication URL
:param user: user name to authenticate as
@@ -975,6 +989,11 @@ class Connection(object):
tenant_name, object_storage_url, region_name
:param insecure: Allow to access insecure keystone server.
The keystone's certificate will not be verified.
+ :param ssl_compression: Whether to enable compression at the SSL layer.
+ If set to 'False' and the pyOpenSSL library is
+ present an attempt to disable SSL compression
+ will be made. This may provide a performance
+ increase for https upload/download operations.
"""
self.authurl = authurl
self.user = user
@@ -992,6 +1011,7 @@ class Connection(object):
self.os_options['tenant_name'] = tenant_name
self.cacert = cacert
self.insecure = insecure
+ self.ssl_compression = ssl_compression
def get_auth(self):
return get_auth(self.authurl,
@@ -1004,7 +1024,8 @@ class Connection(object):
insecure=self.insecure)
def http_connection(self):
- return http_connection(self.url)
+ return http_connection(self.url,
+ ssl_compression=self.ssl_compression)
def _retry(self, reset_func, func, *args, **kwargs):
self.attempts = 0
diff --git a/swiftclient/https_connection.py b/swiftclient/https_connection.py
new file mode 100644
index 0000000..2a2dc1f
--- /dev/null
+++ b/swiftclient/https_connection.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2013 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+HTTPS/SSL related functionality
+"""
+
+import socket
+
+from httplib import HTTPSConnection
+
+import OpenSSL
+
+try:
+ from eventlet.green.OpenSSL.SSL import GreenConnection
+ from eventlet.greenio import GreenSocket
+ from eventlet.patcher import is_monkey_patched
+
+ def getsockopt(self, *args, **kwargs):
+ return self.fd.getsockopt(*args, **kwargs)
+ # The above is a workaround for an eventlet bug in getsockopt.
+ # TODO(mclaren): Workaround can be removed when this fix lands:
+ # https://bitbucket.org/eventlet/eventlet/commits/609f230
+ GreenSocket.getsockopt = getsockopt
+except ImportError:
+ def is_monkey_patched(*args):
+ return False
+
+
+class HTTPSConnectionNoSSLComp(HTTPSConnection):
+ """
+ Extended HTTPSConnection which uses the OpenSSL library
+ for disabling SSL compression.
+ Note: This functionality can eventually be replaced
+ with native Python 3.3 code.
+ """
+ def __init__(self, host):
+ HTTPSConnection.__init__(self, host)
+ self.setcontext()
+
+ def setcontext(self):
+ """
+ Set up the OpenSSL context.
+ """
+ self.context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
+ # Disable SSL layer compression.
+ self.context.set_options(0x20000) # SSL_OP_NO_COMPRESSION
+
+ def connect(self):
+ """
+ Connect to an SSL port using the OpenSSL library and apply
+ per-connection parameters.
+ """
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock = OpenSSLConnectionDelegator(self.context, sock)
+ self.sock.connect((self.host, self.port))
+
+
+class OpenSSLConnectionDelegator(object):
+ """
+ An OpenSSL.SSL.Connection delegator.
+
+ Supplies an additional 'makefile' method which httplib requires
+ and is not present in OpenSSL.SSL.Connection.
+
+ Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
+ a delegator must be used.
+ """
+ def __init__(self, *args, **kwargs):
+ if is_monkey_patched('socket'):
+ # If we are running in a monkey patched environment
+ # use eventlet's GreenConnection -- it handles eventlet's
+ # non-blocking sockets correctly.
+ Connection = GreenConnection
+ else:
+ Connection = OpenSSL.SSL.Connection
+ self.connection = Connection(*args, **kwargs)
+
+ def __getattr__(self, name):
+ return getattr(self.connection, name)
+
+ def makefile(self, *args, **kwargs):
+ return socket._fileobject(self.connection, *args, **kwargs)
diff --git a/tests/test_swiftclient.py b/tests/test_swiftclient.py
index aa0e159..84d165b 100644
--- a/tests/test_swiftclient.py
+++ b/tests/test_swiftclient.py
@@ -14,6 +14,7 @@
# limitations under the License.
# TODO: More tests
+import httplib
import socket
import StringIO
import testtools
@@ -123,7 +124,7 @@ class MockHttpTest(testtools.TestCase):
return_read = kwargs.get('return_read')
query_string = kwargs.get('query_string')
- def wrapper(url, proxy=None):
+ def wrapper(url, proxy=None, ssl_compression=True):
parsed, _conn = _orig_http_connection(url, proxy=proxy)
conn = fake_http_connect(*args, **kwargs)()
@@ -182,7 +183,8 @@ class TestHttpHelpers(MockHttpTest):
self.assertTrue(isinstance(conn, c.HTTPConnection))
url = 'https://www.test.com'
_junk, conn = c.http_connection(url)
- self.assertTrue(isinstance(conn, c.HTTPSConnection))
+ self.assertTrue(isinstance(conn, httplib.HTTPSConnection) or
+ isinstance(conn, c.HTTPSConnectionNoSSLComp))
url = 'ftp://www.test.com'
self.assertRaises(c.ClientException, c.http_connection, url)
@@ -775,7 +777,7 @@ class TestConnection(MockHttpTest):
def read(self, *args, **kwargs):
return ''
- def local_http_connection(url, proxy=None):
+ def local_http_connection(url, proxy=None, ssl_compression=True):
parsed = urlparse(url)
return parsed, LocalConnection()