Add option to disable SSL compression
Allows optionally disabling SSL compression. This can significantly improve HTTPS upload/download performance in some cases -- in particular when the object is not compressible and you have very high network bandwidth. Implements blueprint ssl-compression. Change-Id: I1260055f9c2e83cdabfeb51aed11b3899bed4d55
This commit is contained in:
parent
2d12f09c66
commit
790f087a67
@ -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()
|
||||
|
@ -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
|
||||
|
95
swiftclient/https_connection.py
Normal file
95
swiftclient/https_connection.py
Normal file
@ -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)
|
@ -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()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user