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,
|
os_options=options.os_options,
|
||||||
snet=options.snet,
|
snet=options.snet,
|
||||||
cacert=options.os_cacert,
|
cacert=options.os_cacert,
|
||||||
insecure=options.insecure)
|
insecure=options.insecure,
|
||||||
|
ssl_compression=options.ssl_compression)
|
||||||
|
|
||||||
|
|
||||||
def mkdirs(path):
|
def mkdirs(path):
|
||||||
@ -1268,6 +1269,11 @@ Examples:
|
|||||||
'be verified. '
|
'be verified. '
|
||||||
'Defaults to env[SWIFTCLIENT_INSECURE] '
|
'Defaults to env[SWIFTCLIENT_INSECURE] '
|
||||||
'(set to \'true\' to enable).')
|
'(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()
|
parser.disable_interspersed_args()
|
||||||
(options, args) = parse_args(parser, argv[1:], enforce_requires=False)
|
(options, args) = parse_args(parser, argv[1:], enforce_requires=False)
|
||||||
parser.enable_interspersed_args()
|
parser.enable_interspersed_args()
|
||||||
|
@ -28,6 +28,10 @@ from urlparse import urlparse, urlunparse
|
|||||||
from httplib import HTTPException, HTTPConnection, HTTPSConnection
|
from httplib import HTTPException, HTTPConnection, HTTPSConnection
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
try:
|
||||||
|
from swiftclient.https_connection import HTTPSConnectionNoSSLComp
|
||||||
|
except ImportError:
|
||||||
|
HTTPSConnectionNoSSLComp = HTTPSConnection
|
||||||
|
|
||||||
logger = logging.getLogger("swiftclient")
|
logger = logging.getLogger("swiftclient")
|
||||||
|
|
||||||
@ -141,23 +145,32 @@ class ClientException(Exception):
|
|||||||
return b and '%s: %s' % (a, b) or a
|
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
|
Make an HTTPConnection or HTTPSConnection
|
||||||
|
|
||||||
:param url: url to connect to
|
:param url: url to connect to
|
||||||
:param proxy: proxy to connect through, if any; None by default; str of the
|
:param proxy: proxy to connect through, if any; None by default; str of the
|
||||||
format 'http://127.0.0.1:8888' to set one
|
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)
|
:returns: tuple of (parsed url, connection object)
|
||||||
:raises ClientException: Unable to handle protocol scheme
|
:raises ClientException: Unable to handle protocol scheme
|
||||||
"""
|
"""
|
||||||
url = encode_utf8(url)
|
url = encode_utf8(url)
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
proxy_parsed = urlparse(proxy) if proxy else None
|
proxy_parsed = urlparse(proxy) if proxy else None
|
||||||
|
host = proxy_parsed if proxy else parsed.netloc
|
||||||
if parsed.scheme == 'http':
|
if parsed.scheme == 'http':
|
||||||
conn = HTTPConnection((proxy_parsed if proxy else parsed).netloc)
|
conn = HTTPConnection(host)
|
||||||
elif parsed.scheme == 'https':
|
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:
|
else:
|
||||||
raise ClientException('Cannot handle protocol scheme %s for url %s' %
|
raise ClientException('Cannot handle protocol scheme %s for url %s' %
|
||||||
(parsed.scheme, repr(url)))
|
(parsed.scheme, repr(url)))
|
||||||
@ -956,7 +969,8 @@ class Connection(object):
|
|||||||
def __init__(self, authurl=None, user=None, key=None, retries=5,
|
def __init__(self, authurl=None, user=None, key=None, retries=5,
|
||||||
preauthurl=None, preauthtoken=None, snet=False,
|
preauthurl=None, preauthtoken=None, snet=False,
|
||||||
starting_backoff=1, tenant_name=None, os_options=None,
|
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 authurl: authentication URL
|
||||||
:param user: user name to authenticate as
|
:param user: user name to authenticate as
|
||||||
@ -975,6 +989,11 @@ class Connection(object):
|
|||||||
tenant_name, object_storage_url, region_name
|
tenant_name, object_storage_url, region_name
|
||||||
:param insecure: Allow to access insecure keystone server.
|
:param insecure: Allow to access insecure keystone server.
|
||||||
The keystone's certificate will not be verified.
|
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.authurl = authurl
|
||||||
self.user = user
|
self.user = user
|
||||||
@ -992,6 +1011,7 @@ class Connection(object):
|
|||||||
self.os_options['tenant_name'] = tenant_name
|
self.os_options['tenant_name'] = tenant_name
|
||||||
self.cacert = cacert
|
self.cacert = cacert
|
||||||
self.insecure = insecure
|
self.insecure = insecure
|
||||||
|
self.ssl_compression = ssl_compression
|
||||||
|
|
||||||
def get_auth(self):
|
def get_auth(self):
|
||||||
return get_auth(self.authurl,
|
return get_auth(self.authurl,
|
||||||
@ -1004,7 +1024,8 @@ class Connection(object):
|
|||||||
insecure=self.insecure)
|
insecure=self.insecure)
|
||||||
|
|
||||||
def http_connection(self):
|
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):
|
def _retry(self, reset_func, func, *args, **kwargs):
|
||||||
self.attempts = 0
|
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.
|
# limitations under the License.
|
||||||
|
|
||||||
# TODO: More tests
|
# TODO: More tests
|
||||||
|
import httplib
|
||||||
import socket
|
import socket
|
||||||
import StringIO
|
import StringIO
|
||||||
import testtools
|
import testtools
|
||||||
@ -123,7 +124,7 @@ class MockHttpTest(testtools.TestCase):
|
|||||||
return_read = kwargs.get('return_read')
|
return_read = kwargs.get('return_read')
|
||||||
query_string = kwargs.get('query_string')
|
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)
|
parsed, _conn = _orig_http_connection(url, proxy=proxy)
|
||||||
conn = fake_http_connect(*args, **kwargs)()
|
conn = fake_http_connect(*args, **kwargs)()
|
||||||
|
|
||||||
@ -182,7 +183,8 @@ class TestHttpHelpers(MockHttpTest):
|
|||||||
self.assertTrue(isinstance(conn, c.HTTPConnection))
|
self.assertTrue(isinstance(conn, c.HTTPConnection))
|
||||||
url = 'https://www.test.com'
|
url = 'https://www.test.com'
|
||||||
_junk, conn = c.http_connection(url)
|
_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'
|
url = 'ftp://www.test.com'
|
||||||
self.assertRaises(c.ClientException, c.http_connection, url)
|
self.assertRaises(c.ClientException, c.http_connection, url)
|
||||||
|
|
||||||
@ -775,7 +777,7 @@ class TestConnection(MockHttpTest):
|
|||||||
def read(self, *args, **kwargs):
|
def read(self, *args, **kwargs):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def local_http_connection(url, proxy=None):
|
def local_http_connection(url, proxy=None, ssl_compression=True):
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
return parsed, LocalConnection()
|
return parsed, LocalConnection()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user