Port to python-requests
Currently, httplib implementation does not support SSL certificate verification. This patch fixes this. Note that ssl compression parameter and 100-continue thing is still missing from requests, though those are lower priority. Requests now takes care of: * proxy configuration (get_environ_proxies), * chunked encoding (with data generator), * bulk uploading (with files dictionary), * SSL certificate verification (with 'insecure' and 'cacert' parameter). This patch have been tested with requests 1.1.0 (CentOS 6) and requests 2.2.1 (current version). Change-Id: Ib5de962f4102d57c71ad85fd81a615362ef175dc Closes-Bug: #1199783 DocImpact SecurityImpact
This commit is contained in:
parent
9b73547b7d
commit
b182112719
14
bin/swift
14
bin/swift
|
@ -32,7 +32,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from swiftclient import Connection, HTTPException
|
from swiftclient import Connection, RequestException
|
||||||
from swiftclient import command_helpers
|
from swiftclient import command_helpers
|
||||||
from swiftclient.utils import config_true_value, prt_bytes
|
from swiftclient.utils import config_true_value, prt_bytes
|
||||||
from swiftclient.multithreading import MultiThreadingManager
|
from swiftclient.multithreading import MultiThreadingManager
|
||||||
|
@ -1388,16 +1388,16 @@ Examples:
|
||||||
parser.add_option('--insecure',
|
parser.add_option('--insecure',
|
||||||
action="store_true", dest="insecure",
|
action="store_true", dest="insecure",
|
||||||
default=default_val,
|
default=default_val,
|
||||||
help='Allow swiftclient to access insecure keystone '
|
help='Allow swiftclient to access servers without '
|
||||||
'server. The keystone\'s certificate will not '
|
'having to verify the SSL certificate. '
|
||||||
'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',
|
parser.add_option('--no-ssl-compression',
|
||||||
action='store_false', dest='ssl_compression',
|
action='store_false', dest='ssl_compression',
|
||||||
default=True,
|
default=True,
|
||||||
help='Disable SSL compression when using https. '
|
help='This option is deprecated and not used anymore. '
|
||||||
'This may increase performance.')
|
'SSL compression should be disabled by default '
|
||||||
|
'by the system SSL library')
|
||||||
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()
|
||||||
|
@ -1425,7 +1425,7 @@ Examples:
|
||||||
parser.usage = globals()['st_%s_help' % args[0]]
|
parser.usage = globals()['st_%s_help' % args[0]]
|
||||||
try:
|
try:
|
||||||
globals()['st_%s' % args[0]](parser, argv[1:], thread_manager)
|
globals()['st_%s' % args[0]](parser, argv[1:], thread_manager)
|
||||||
except (ClientException, HTTPException, socket.error) as err:
|
except (ClientException, RequestException, socket.error) as err:
|
||||||
thread_manager.error(str(err))
|
thread_manager.error(str(err))
|
||||||
|
|
||||||
had_error = thread_manager.error_count
|
had_error = thread_manager.error_count
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
requests>=1.1
|
||||||
simplejson>=2.0.9
|
simplejson>=2.0.9
|
||||||
|
|
|
@ -18,24 +18,18 @@ OpenStack Swift client library used internally
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
import requests
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
|
from distutils.version import StrictVersion
|
||||||
|
from requests.exceptions import RequestException, SSLError
|
||||||
from urllib import quote as _quote
|
from urllib import quote as _quote
|
||||||
from urlparse import urlparse, urlunparse
|
from urlparse import urlparse, urlunparse
|
||||||
from httplib import HTTPException, HTTPConnection, HTTPSConnection
|
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
|
|
||||||
from swiftclient.exceptions import ClientException, InvalidHeadersException
|
from swiftclient.exceptions import ClientException, InvalidHeadersException
|
||||||
from swiftclient.utils import get_environ_proxies
|
|
||||||
|
|
||||||
try:
|
|
||||||
from swiftclient.https_connection import HTTPSConnectionNoSSLComp
|
|
||||||
except ImportError:
|
|
||||||
HTTPSConnectionNoSSLComp = HTTPSConnection
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from logging import NullHandler
|
from logging import NullHandler
|
||||||
|
@ -50,6 +44,18 @@ except ImportError:
|
||||||
def createLock(self):
|
def createLock(self):
|
||||||
self.lock = None
|
self.lock = None
|
||||||
|
|
||||||
|
# requests version 1.2.3 try to encode headers in ascii, preventing
|
||||||
|
# utf-8 encoded header to be 'prepared'
|
||||||
|
if StrictVersion(requests.__version__) < StrictVersion('2.0.0'):
|
||||||
|
from requests.structures import CaseInsensitiveDict
|
||||||
|
|
||||||
|
def prepare_unicode_headers(self, headers):
|
||||||
|
if headers:
|
||||||
|
self.headers = CaseInsensitiveDict(headers)
|
||||||
|
else:
|
||||||
|
self.headers = CaseInsensitiveDict()
|
||||||
|
requests.models.PreparedRequest.prepare_headers = prepare_unicode_headers
|
||||||
|
|
||||||
logger = logging.getLogger("swiftclient")
|
logger = logging.getLogger("swiftclient")
|
||||||
logger.addHandler(NullHandler())
|
logger.addHandler(NullHandler())
|
||||||
|
|
||||||
|
@ -124,68 +130,93 @@ except ImportError:
|
||||||
from json import loads as json_loads
|
from json import loads as json_loads
|
||||||
|
|
||||||
|
|
||||||
def http_connection(url, proxy=None, ssl_compression=True):
|
class HTTPConnection:
|
||||||
"""
|
def __init__(self, url, proxy=None, cacert=None, insecure=False,
|
||||||
Make an HTTPConnection or HTTPSConnection
|
ssl_compression=False):
|
||||||
|
"""
|
||||||
|
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
|
||||||
format 'http://127.0.0.1:8888' to set one
|
of the format 'http://127.0.0.1:8888' to set one
|
||||||
:param ssl_compression: Whether to enable compression at the SSL layer.
|
:param cacert: A CA bundle file to use in verifying a TLS server
|
||||||
If set to 'False' and the pyOpenSSL library is
|
certificate.
|
||||||
present an attempt to disable SSL compression
|
:param insecure: Allow to access servers without checking SSL certs.
|
||||||
will be made. This may provide a performance
|
The server's certificate will not be verified.
|
||||||
increase for https upload/download operations.
|
:param ssl_compression: SSL compression should be disabled by default
|
||||||
:returns: tuple of (parsed url, connection object)
|
and this setting is not usable as of now. The
|
||||||
:raises ClientException: Unable to handle protocol scheme
|
parameter is kept for backward compatibility.
|
||||||
"""
|
:raises ClientException: Unable to handle protocol scheme
|
||||||
url = encode_utf8(url)
|
"""
|
||||||
parsed = urlparse(url)
|
self.url = url
|
||||||
if proxy:
|
self.parsed_url = urlparse(url)
|
||||||
proxy_parsed = urlparse(proxy)
|
self.host = self.parsed_url.netloc
|
||||||
else:
|
self.port = self.parsed_url.port
|
||||||
proxies = get_environ_proxies(parsed.netloc)
|
self.requests_args = {}
|
||||||
proxy = proxies.get(parsed.scheme, None)
|
if self.parsed_url.scheme not in ('http', 'https'):
|
||||||
proxy_parsed = urlparse(proxy) if proxy else None
|
raise ClientException("Unsupported scheme")
|
||||||
host = proxy_parsed.netloc if proxy else parsed.netloc
|
self.requests_args['verify'] = not insecure
|
||||||
if parsed.scheme == 'http':
|
if cacert:
|
||||||
conn = HTTPConnection(host)
|
# verify requests parameter is used to pass the CA_BUNDLE file
|
||||||
elif parsed.scheme == 'https':
|
# see: http://docs.python-requests.org/en/latest/user/advanced/
|
||||||
if ssl_compression is True:
|
self.requests_args['verify'] = cacert
|
||||||
conn = HTTPSConnection(host)
|
if proxy:
|
||||||
else:
|
proxy_parsed = urlparse(proxy)
|
||||||
conn = HTTPSConnectionNoSSLComp(host)
|
if not proxy_parsed.scheme:
|
||||||
else:
|
raise ClientException("Proxy's missing scheme")
|
||||||
raise ClientException('Cannot handle protocol scheme %s for url %s' %
|
self.requests_args['proxies'] = {
|
||||||
(parsed.scheme, repr(url)))
|
proxy_parsed.scheme: '%s://%s' % (
|
||||||
|
proxy_parsed.scheme, proxy_parsed.netloc
|
||||||
|
)
|
||||||
|
}
|
||||||
|
self.requests_args['stream'] = True
|
||||||
|
|
||||||
def putheader_wrapper(func):
|
def _request(self, *arg, **kwarg):
|
||||||
|
""" Final wrapper before requests call, to be patched in tests """
|
||||||
|
return requests.request(*arg, **kwarg)
|
||||||
|
|
||||||
@wraps(func)
|
def request(self, method, full_path, data=None, headers={}, files=None):
|
||||||
def putheader_escaped(key, value):
|
""" Encode url and header, then call requests.request """
|
||||||
func(encode_utf8(key), encode_utf8(value))
|
headers = dict((encode_utf8(x), encode_utf8(y)) for x, y in
|
||||||
return putheader_escaped
|
headers.iteritems())
|
||||||
conn.putheader = putheader_wrapper(conn.putheader)
|
url = encode_utf8("%s://%s%s" % (
|
||||||
|
self.parsed_url.scheme,
|
||||||
|
self.parsed_url.netloc,
|
||||||
|
full_path))
|
||||||
|
self.resp = self._request(method, url, headers=headers, data=data,
|
||||||
|
files=files, **self.requests_args)
|
||||||
|
return self.resp
|
||||||
|
|
||||||
def request_wrapper(func):
|
def putrequest(self, full_path, data=None, headers={}, files=None):
|
||||||
|
"""
|
||||||
|
Use python-requests files upload
|
||||||
|
|
||||||
@wraps(func)
|
:param data: Use data generator for chunked-transfer
|
||||||
def request_escaped(method, url, body=None, headers=None):
|
:param files: Use files for default transfer
|
||||||
validate_headers(headers)
|
"""
|
||||||
url = encode_utf8(url)
|
return self.request('PUT', full_path, data, headers, files)
|
||||||
if body:
|
|
||||||
body = encode_utf8(body)
|
def getresponse(self):
|
||||||
func(method, url, body=body, headers=headers or {})
|
""" Adapt requests response to httplib interface """
|
||||||
return request_escaped
|
self.resp.status = self.resp.status_code
|
||||||
conn.request = request_wrapper(conn.request)
|
old_getheader = self.resp.raw.getheader
|
||||||
if proxy:
|
|
||||||
try:
|
def getheaders():
|
||||||
# python 2.6 method
|
return self.resp.headers.items()
|
||||||
conn._set_tunnel(parsed.hostname, parsed.port)
|
|
||||||
except AttributeError:
|
def getheader(k, v=None):
|
||||||
# python 2.7 method
|
return old_getheader(k.lower(), v)
|
||||||
conn.set_tunnel(parsed.hostname, parsed.port)
|
|
||||||
return parsed, conn
|
self.resp.getheaders = getheaders
|
||||||
|
self.resp.getheader = getheader
|
||||||
|
self.resp.read = self.resp.raw.read
|
||||||
|
return self.resp
|
||||||
|
|
||||||
|
|
||||||
|
def http_connection(*arg, **kwarg):
|
||||||
|
""" :returns: tuple of (parsed url, connection object) """
|
||||||
|
conn = HTTPConnection(*arg, **kwarg)
|
||||||
|
return conn.parsed_url, conn
|
||||||
|
|
||||||
|
|
||||||
def get_auth_1_0(url, user, key, snet):
|
def get_auth_1_0(url, user, key, snet):
|
||||||
|
@ -890,27 +921,16 @@ def put_object(url, token=None, container=None, name=None, contents=None,
|
||||||
if hasattr(contents, 'read'):
|
if hasattr(contents, 'read'):
|
||||||
if chunk_size is None:
|
if chunk_size is None:
|
||||||
chunk_size = 65536
|
chunk_size = 65536
|
||||||
conn.putrequest('PUT', path)
|
|
||||||
for header, value in headers.iteritems():
|
|
||||||
conn.putheader(header, value)
|
|
||||||
if content_length is None:
|
if content_length is None:
|
||||||
conn.putheader('Transfer-Encoding', 'chunked')
|
def chunk_reader():
|
||||||
conn.endheaders()
|
while True:
|
||||||
chunk = contents.read(chunk_size)
|
data = contents.read(chunk_size)
|
||||||
while chunk:
|
if not data:
|
||||||
conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
|
break
|
||||||
chunk = contents.read(chunk_size)
|
yield data
|
||||||
conn.send('0\r\n\r\n')
|
conn.putrequest(path, headers=headers, data=chunk_reader())
|
||||||
else:
|
else:
|
||||||
conn.endheaders()
|
conn.putrequest(path, headers=headers, files={"file": contents})
|
||||||
left = content_length
|
|
||||||
while left > 0:
|
|
||||||
size = chunk_size
|
|
||||||
if size > left:
|
|
||||||
size = left
|
|
||||||
chunk = contents.read(size)
|
|
||||||
conn.send(chunk)
|
|
||||||
left -= len(chunk)
|
|
||||||
else:
|
else:
|
||||||
if chunk_size is not None:
|
if chunk_size is not None:
|
||||||
warn_msg = '%s object has no \"read\" method, ignoring chunk_size'\
|
warn_msg = '%s object has no \"read\" method, ignoring chunk_size'\
|
||||||
|
@ -1129,6 +1149,8 @@ class Connection(object):
|
||||||
|
|
||||||
def http_connection(self):
|
def http_connection(self):
|
||||||
return http_connection(self.url,
|
return http_connection(self.url,
|
||||||
|
cacert=self.cacert,
|
||||||
|
insecure=self.insecure,
|
||||||
ssl_compression=self.ssl_compression)
|
ssl_compression=self.ssl_compression)
|
||||||
|
|
||||||
def _add_response_dict(self, target_dict, kwargs):
|
def _add_response_dict(self, target_dict, kwargs):
|
||||||
|
@ -1160,7 +1182,9 @@ class Connection(object):
|
||||||
rv = func(self.url, self.token, *args, **kwargs)
|
rv = func(self.url, self.token, *args, **kwargs)
|
||||||
self._add_response_dict(caller_response_dict, kwargs)
|
self._add_response_dict(caller_response_dict, kwargs)
|
||||||
return rv
|
return rv
|
||||||
except (socket.error, HTTPException) as e:
|
except SSLError:
|
||||||
|
raise
|
||||||
|
except (socket.error, RequestException) as e:
|
||||||
self._add_response_dict(caller_response_dict, kwargs)
|
self._add_response_dict(caller_response_dict, kwargs)
|
||||||
if self.attempts > self.retries:
|
if self.attempts > self.retries:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
# 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,23 +14,6 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Miscellaneous utility functions for use with Swift."""
|
"""Miscellaneous utility functions for use with Swift."""
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
_ver = sys.version_info
|
|
||||||
|
|
||||||
#: Python 2.x?
|
|
||||||
is_py2 = (_ver[0] == 2)
|
|
||||||
|
|
||||||
#: Python 3.x?
|
|
||||||
is_py3 = (_ver[0] == 3)
|
|
||||||
|
|
||||||
if is_py2:
|
|
||||||
from urllib import getproxies, proxy_bypass
|
|
||||||
elif is_py3:
|
|
||||||
from urllib.request import getproxies, proxy_bypass
|
|
||||||
|
|
||||||
|
|
||||||
TRUE_VALUES = set(('true', '1', 'yes', 'on', 't', 'y'))
|
TRUE_VALUES = set(('true', '1', 'yes', 'on', 't', 'y'))
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,36 +55,3 @@ def prt_bytes(bytes, human_flag):
|
||||||
bytes = '%12s' % bytes
|
bytes = '%12s' % bytes
|
||||||
|
|
||||||
return(bytes)
|
return(bytes)
|
||||||
|
|
||||||
|
|
||||||
# get_environ_proxies function, borrowed from python Requests
|
|
||||||
# (www.python-requests.org)
|
|
||||||
def get_environ_proxies(netloc):
|
|
||||||
"""Return a dict of environment proxies."""
|
|
||||||
|
|
||||||
get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper())
|
|
||||||
|
|
||||||
# First check whether no_proxy is defined. If it is, check that the URL
|
|
||||||
# we're getting isn't in the no_proxy list.
|
|
||||||
no_proxy = get_proxy('no_proxy')
|
|
||||||
|
|
||||||
if no_proxy:
|
|
||||||
# We need to check whether we match here. We need to see if we match
|
|
||||||
# the end of the netloc, both with and without the port.
|
|
||||||
no_proxy = no_proxy.replace(' ', '').split(',')
|
|
||||||
|
|
||||||
for host in no_proxy:
|
|
||||||
if netloc.endswith(host) or netloc.split(':')[0].endswith(host):
|
|
||||||
# The URL does match something in no_proxy, so we don't want
|
|
||||||
# to apply the proxies on this URL.
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# If the system proxy settings indicate that this URL should be bypassed,
|
|
||||||
# don't proxy.
|
|
||||||
if proxy_bypass(netloc):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# If we get here, we either didn't have no_proxy set or we're not going
|
|
||||||
# anywhere that no_proxy applies to, and the system settings don't require
|
|
||||||
# bypassing the proxy for the current URL.
|
|
||||||
return getproxies()
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
# TODO: More tests
|
# TODO: More tests
|
||||||
import mock
|
import mock
|
||||||
import httplib
|
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
import StringIO
|
import StringIO
|
||||||
|
@ -107,7 +106,8 @@ class MockHttpTest(testtools.TestCase):
|
||||||
query_string = kwargs.get('query_string')
|
query_string = kwargs.get('query_string')
|
||||||
storage_url = kwargs.get('storage_url')
|
storage_url = kwargs.get('storage_url')
|
||||||
|
|
||||||
def wrapper(url, proxy=None, ssl_compression=True):
|
def wrapper(url, proxy=None, cacert=None, insecure=False,
|
||||||
|
ssl_compression=True):
|
||||||
if storage_url:
|
if storage_url:
|
||||||
self.assertEqual(storage_url, url)
|
self.assertEqual(storage_url, url)
|
||||||
|
|
||||||
|
@ -138,11 +138,17 @@ class MockHttpTest(testtools.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class MockHttpResponse():
|
class MockHttpResponse():
|
||||||
def __init__(self):
|
def __init__(self, status=0):
|
||||||
self.status = 200
|
self.status = status
|
||||||
|
self.status_code = status
|
||||||
self.reason = "OK"
|
self.reason = "OK"
|
||||||
self.buffer = []
|
self.buffer = []
|
||||||
|
|
||||||
|
class Raw:
|
||||||
|
def read():
|
||||||
|
pass
|
||||||
|
self.raw = Raw()
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -153,10 +159,15 @@ class MockHttpResponse():
|
||||||
return {"key1": "value1", "key2": "value2"}
|
return {"key1": "value1", "key2": "value2"}
|
||||||
|
|
||||||
def fake_response(self):
|
def fake_response(self):
|
||||||
return MockHttpResponse()
|
return MockHttpResponse(self.status)
|
||||||
|
|
||||||
def fake_send(self, msg):
|
def _fake_request(self, *arg, **kwarg):
|
||||||
self.buffer.append(msg)
|
self.status = 200
|
||||||
|
# This simulate previous httplib implementation that would do a
|
||||||
|
# putrequest() and then use putheader() to send header.
|
||||||
|
for k, v in kwarg['headers'].iteritems():
|
||||||
|
self.buffer.append('%s: %s' % (k, v))
|
||||||
|
return self.fake_response()
|
||||||
|
|
||||||
|
|
||||||
class TestHttpHelpers(MockHttpTest):
|
class TestHttpHelpers(MockHttpTest):
|
||||||
|
@ -173,8 +184,7 @@ 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, httplib.HTTPSConnection) or
|
self.assertTrue(isinstance(conn, c.HTTPConnection))
|
||||||
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)
|
||||||
|
|
||||||
|
@ -560,7 +570,7 @@ class TestPutObject(MockHttpTest):
|
||||||
|
|
||||||
resp = MockHttpResponse()
|
resp = MockHttpResponse()
|
||||||
conn[1].getresponse = resp.fake_response
|
conn[1].getresponse = resp.fake_response
|
||||||
conn[1].send = resp.fake_send
|
conn[1]._request = resp._fake_request
|
||||||
value = c.put_object(*args, headers=headers, http_conn=conn)
|
value = c.put_object(*args, headers=headers, http_conn=conn)
|
||||||
self.assertTrue(isinstance(value, basestring))
|
self.assertTrue(isinstance(value, basestring))
|
||||||
# Test for RFC-2616 encoded symbols
|
# Test for RFC-2616 encoded symbols
|
||||||
|
@ -573,7 +583,7 @@ class TestPutObject(MockHttpTest):
|
||||||
args = ('asdf', 'asdf', 'asdf', 'asdf', mock_file)
|
args = ('asdf', 'asdf', 'asdf', 'asdf', mock_file)
|
||||||
resp = MockHttpResponse()
|
resp = MockHttpResponse()
|
||||||
conn[1].getresponse = resp.fake_response
|
conn[1].getresponse = resp.fake_response
|
||||||
conn[1].send = resp.fake_send
|
conn[1]._request = resp._fake_request
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
c.put_object(*args, chunk_size=20, headers={}, http_conn=conn)
|
c.put_object(*args, chunk_size=20, headers={}, http_conn=conn)
|
||||||
self.assertEqual(len(w), 0)
|
self.assertEqual(len(w), 0)
|
||||||
|
@ -621,7 +631,7 @@ class TestPostObject(MockHttpTest):
|
||||||
|
|
||||||
resp = MockHttpResponse()
|
resp = MockHttpResponse()
|
||||||
conn[1].getresponse = resp.fake_response
|
conn[1].getresponse = resp.fake_response
|
||||||
conn[1].send = resp.fake_send
|
conn[1]._request = resp._fake_request
|
||||||
c.post_object(*args, headers=headers, http_conn=conn)
|
c.post_object(*args, headers=headers, http_conn=conn)
|
||||||
# Test for RFC-2616 encoded symbols
|
# Test for RFC-2616 encoded symbols
|
||||||
self.assertTrue("a-b: .x:yz mn:kl:qr" in resp.buffer[0],
|
self.assertTrue("a-b: .x:yz mn:kl:qr" in resp.buffer[0],
|
||||||
|
@ -853,7 +863,7 @@ class TestConnection(MockHttpTest):
|
||||||
self.port = parsed_url.netloc
|
self.port = parsed_url.netloc
|
||||||
|
|
||||||
def putrequest(self, *args, **kwargs):
|
def putrequest(self, *args, **kwargs):
|
||||||
return
|
self.send()
|
||||||
|
|
||||||
def putheader(self, *args, **kwargs):
|
def putheader(self, *args, **kwargs):
|
||||||
return
|
return
|
||||||
|
@ -880,7 +890,8 @@ class TestConnection(MockHttpTest):
|
||||||
def read(self, *args, **kwargs):
|
def read(self, *args, **kwargs):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def local_http_connection(url, proxy=None, ssl_compression=True):
|
def local_http_connection(url, proxy=None, cacert=None,
|
||||||
|
insecure=False, ssl_compression=True):
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
return parsed, LocalConnection()
|
return parsed, LocalConnection()
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import testtools
|
import testtools
|
||||||
import os
|
|
||||||
|
|
||||||
from swiftclient import utils as u
|
from swiftclient import utils as u
|
||||||
|
|
||||||
|
@ -118,82 +117,3 @@ class TestPrtBytes(testtools.TestCase):
|
||||||
def test_overflow(self):
|
def test_overflow(self):
|
||||||
bytes_ = 2 ** 90
|
bytes_ = 2 ** 90
|
||||||
self.assertEqual('1024Y', u.prt_bytes(bytes_, True).lstrip())
|
self.assertEqual('1024Y', u.prt_bytes(bytes_, True).lstrip())
|
||||||
|
|
||||||
|
|
||||||
class TestGetEnvironProxy(testtools.TestCase):
|
|
||||||
|
|
||||||
ENV_VARS = ('http_proxy', 'https_proxy', 'no_proxy',
|
|
||||||
'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY')
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.proxy_dict = {}
|
|
||||||
super(TestGetEnvironProxy, self).setUp()
|
|
||||||
for proxy_s in TestGetEnvironProxy.ENV_VARS:
|
|
||||||
# Save old env value
|
|
||||||
self.proxy_dict[proxy_s] = os.environ.get(proxy_s, None)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
super(TestGetEnvironProxy, self).tearDown()
|
|
||||||
for proxy_s in TestGetEnvironProxy.ENV_VARS:
|
|
||||||
if self.proxy_dict[proxy_s]:
|
|
||||||
os.environ[proxy_s] = self.proxy_dict[proxy_s]
|
|
||||||
elif os.environ.get(proxy_s):
|
|
||||||
del os.environ[proxy_s]
|
|
||||||
|
|
||||||
def setup_env(self, new_env={}):
|
|
||||||
for proxy_s in TestGetEnvironProxy.ENV_VARS:
|
|
||||||
# Set new env value
|
|
||||||
if new_env.get(proxy_s):
|
|
||||||
os.environ[proxy_s] = new_env.get(proxy_s)
|
|
||||||
elif os.environ.get(proxy_s):
|
|
||||||
del os.environ[proxy_s]
|
|
||||||
|
|
||||||
def test_http_proxy(self):
|
|
||||||
self.setup_env({'http_proxy': 'http://proxy.tests.com:8080'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(proxy_dict['http'], 'http://proxy.tests.com:8080')
|
|
||||||
self.assertEqual(proxy_dict.get('https'), None)
|
|
||||||
self.assertEqual(len(proxy_dict), 1)
|
|
||||||
self.setup_env({'HTTP_PROXY': 'http://proxy.tests.com:8080'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(proxy_dict['http'], 'http://proxy.tests.com:8080')
|
|
||||||
self.assertEqual(proxy_dict.get('https'), None)
|
|
||||||
self.assertEqual(len(proxy_dict), 1)
|
|
||||||
|
|
||||||
def test_https_proxy(self):
|
|
||||||
self.setup_env({'https_proxy': 'http://proxy.tests.com:8080'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(proxy_dict['https'], 'http://proxy.tests.com:8080')
|
|
||||||
self.assertEqual(proxy_dict.get('http'), None)
|
|
||||||
self.assertEqual(len(proxy_dict), 1)
|
|
||||||
self.setup_env({'HTTPS_PROXY': 'http://proxy.tests.com:8080'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(proxy_dict['https'], 'http://proxy.tests.com:8080')
|
|
||||||
self.assertEqual(proxy_dict.get('http'), None)
|
|
||||||
self.assertEqual(len(proxy_dict), 1)
|
|
||||||
|
|
||||||
def test_http_https_proxy(self):
|
|
||||||
self.setup_env({'http_proxy': 'http://proxy1.tests.com:8081',
|
|
||||||
'https_proxy': 'http://proxy2.tests.com:8082'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(proxy_dict['http'], 'http://proxy1.tests.com:8081')
|
|
||||||
self.assertEqual(proxy_dict['https'], 'http://proxy2.tests.com:8082')
|
|
||||||
self.assertEqual(len(proxy_dict), 2)
|
|
||||||
self.setup_env({'http_proxy': 'http://proxy1.tests.com:8081',
|
|
||||||
'HTTPS_PROXY': 'http://proxy2.tests.com:8082'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(proxy_dict['http'], 'http://proxy1.tests.com:8081')
|
|
||||||
self.assertEqual(proxy_dict['https'], 'http://proxy2.tests.com:8082')
|
|
||||||
self.assertEqual(len(proxy_dict), 2)
|
|
||||||
|
|
||||||
def test_proxy_exclusion(self):
|
|
||||||
self.setup_env({'http_proxy': 'http://proxy1.tests.com:8081',
|
|
||||||
'https_proxy': 'http://proxy2.tests.com:8082',
|
|
||||||
'no_proxy': 'www.tests.com'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(len(proxy_dict), 0)
|
|
||||||
self.setup_env({'http_proxy': 'http://proxy1.tests.com:8081',
|
|
||||||
'HTTPS_PROXY': 'http://proxy2.tests.com:8082',
|
|
||||||
'NO_PROXY': 'www.tests.com'})
|
|
||||||
proxy_dict = u.get_environ_proxies('www.tests.com:81')
|
|
||||||
self.assertEqual(len(proxy_dict), 0)
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
# implied.
|
# implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
from httplib import HTTPException
|
from requests import RequestException
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ def fake_http_connect(*code_iter, **kwargs):
|
||||||
|
|
||||||
def getexpect(self):
|
def getexpect(self):
|
||||||
if self.status == -2:
|
if self.status == -2:
|
||||||
raise HTTPException()
|
raise RequestException()
|
||||||
if self.status == -3:
|
if self.status == -3:
|
||||||
return FakeConn(507)
|
return FakeConn(507)
|
||||||
return FakeConn(100)
|
return FakeConn(100)
|
||||||
|
@ -141,7 +141,7 @@ def fake_http_connect(*code_iter, **kwargs):
|
||||||
etag = etag_iter.next()
|
etag = etag_iter.next()
|
||||||
timestamp = timestamps_iter.next()
|
timestamp = timestamps_iter.next()
|
||||||
if status <= 0:
|
if status <= 0:
|
||||||
raise HTTPException()
|
raise RequestException()
|
||||||
fake_conn = FakeConn(status, etag, body=kwargs.get('body', ''),
|
fake_conn = FakeConn(status, etag, body=kwargs.get('body', ''),
|
||||||
timestamp=timestamp)
|
timestamp=timestamp)
|
||||||
fake_conn.connect()
|
fake_conn.connect()
|
||||||
|
|
Loading…
Reference in New Issue