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:
Tristan Cacqueray 2014-01-24 17:40:16 +01:00
parent 9b73547b7d
commit b182112719
8 changed files with 146 additions and 335 deletions

View File

@ -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

View File

@ -1 +1,2 @@
requests>=1.1
simplejson>=2.0.9 simplejson>=2.0.9

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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()