Snap for 5999261 from 02e254aba92fb784f656f1285bddf6d225a60a52 to r-keystone-qcom-release

Change-Id: Iedbf58311453944ed6d769ac8ffc344610d80fba
diff --git a/.gitignore b/.gitignore
index af48444..313decf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
 .project
 .pydevproject
 .pytest_cache/*
+.tags*
 .tox
 _httplib2_test_cache/*
 bin
diff --git a/.travis.yml b/.travis.yml
index 57991d5..ca91b60 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,6 @@
     - pip_install_common='pip>=9.0 setuptools>=36.2 wheel>=0.30'
 python:
   - 2.7
-  - 3.4
   - 3.5
   - 3.6
   - 3.7
@@ -39,23 +38,32 @@
       script: test_group=pep8 script/test
     - stage: test
       env: _=py3-pep8
-      python: 3.6
+      python: 3.7
       install: pip install -r requirements-test.txt
       script: test_group=pep8 script/test
     - stage: test
+      env: _-py37-openssl11
+      python: 3.7 # we'll use compiled version but makes Travis look correct
+      install:
+        - deactivate && rm -rf $HOME/virtualenv
+        - source script/compile-py3-openssl11.sh # source to alter PATH
+        - pip install virtualenv && virtualenv $HOME/virtualenv && source $HOME/virtualenv/bin/activate
+        - pip install $pip_install_common -r requirements-test.txt
+      script: script/test -sv
+    - stage: test
       env: _=py2-package
       python: 2.7
       install: pip install $pip_install_common
       script: test_group=package script/test
     - stage: test
       env: _=py3-package
-      python: 3.6
+      python: 3.7
       install: pip install $pip_install_common
       script: test_group=package script/test
     - stage: release
       if: (branch = master)
       env: _=pypi-upload-test
-      python: 3.6
+      python: 3.7
       install: pip install $pip_install_common
       script: script/release -auto
       deploy:
@@ -64,16 +72,14 @@
         user: httplib2.release.test
         password:
           secure: "XN3oxobC+26UPiS+F1MvL4c6XtytejZ13SkLXCHfgVDPSASzKqF81CnR4EhsnbfZLvSgGHgSlfY5Jve5HF2VR9GzpJMc6wzcfkkeBg6PeRHuMppIqmuoq7BTw81SZL9X62/mju/vxXs2cHpVkwNTSE7W1JH1bVXPj86oAR9xXo9waRuXcyPGNuSqmOd1NPOMbFmeuz+HeArk2Fz7wwo6H5BJuXjOrEOHWD1rzeRchH901PBUrftm54Id2TIVMARW8jm1saQY2FtPWaBv2v/DJC1fKWMJpcNQ3mmcvrrTFC1IJ00dk9XJfqx5hnsRaergc0UvzHoOGEQKSMdg0PUAkvNohAoCf+3GddPkvk8MaZ+aQlijoK6wp93A5dfTxBVZqdhmEdheolbYiJPunzS60bWvaEv6/D15/xyMiwGamUmF1Tx7UIvvm/zj6tAOBWbNEgLRyvQ0qx2RE95GLtp+RXK4pT+Kig1+cof5hrWODuEl1SSLMBySaNLWO73IN9esZu0X1JS7svnROLRJCAvRjppJYswwCPziP+B8XQDeMrhIDMHNzdbtxOPpBAXpYUE764FkzaUTMsK83Q+ugE3Dx8xtrAzT4M0fdiFv+3QEhSUtfvWsLH9zS9wXC5Px9kPKU3FO8mdUyf7A0bIasvJLNcApDJigKjBukToOIsZVFok="
-        # TODO: sdist bdist_wheel
-        # but wheels don't roll well with our 2/3 split code base
-        distributions: "sdist"
+        distributions: "sdist bdist_wheel"
         skip_cleanup: true
         on:
           repo: httplib2/httplib2
     - stage: release
       if: (tag IS present)
       env: _=pypi-upload-public
-      python: 3.6
+      python: 3.7
       install: pip install $pip_install_common
       script: script/release -auto
       deploy:
@@ -81,9 +87,7 @@
         user: httplib2.release
         password:
           secure: "jZAyMFnmbhYChjsb3gRYfESWlio6pgmWEWBRxtBQXYZf+tzyKVISyHuyWkJvOVTP+lOpp2MTPZ2s1UgxGwfzZ/VE034Cz5iA/C6wafmgtSW+wK+KEJFPseHBBA7Gh4ReiAPi2a+i1UXdsJpFNhv36E9tbTq2sEinbisE2lSEQ0KHadjkc+6pvCjlyhmes7QyM5GviWYlWRNj2OIkT8SUuUcWQt7ZEl6kN82MoMHCaf1YxE/i4JUP3VLomWK3RLZJP356Y4IDkzlVhFU4MJ4ubNtoQ/ECM0uQ+nsHzO0k1uGWdF6mMTna7U5gLqUi9rfCK3bLMeVSo+TUCpjI7HkWDaBgVXGTe5dUMJCDfRgqeYa0GnriI74XYJu8NGjMLv30uO58t9E7VQGo2NrFRJDzKAIHANejWnpUPY3XgoN1rlrh52seMjaU2+jO40EC8HvIqeRRwPwhkqCSV2y+IZT2bOFp2nbMWhkUMsxIX7OXt+sy8GvK/ilMleEl7r0tnudsT7lGdnMwXlttI3CIAFGE7E+0zwnxNiMzQDzo+ILVR7ezrCK9M9xVYKGa3i8gkpSn0Fblnltgg7HaEI8YC3rMZe4iu1t0D6cZZUAAp2ZUo3NCJcZ35iUFBhlFvjVDbe2upJgU6GFgtDLjyzCJiKbz8qLRgMFYgT0CGr512e1jBo0="
-        # TODO: sdist bdist_wheel
-        # but wheels don't roll well with our 2/3 split code base
-        distributions: "sdist"
+        distributions: "sdist bdist_wheel"
         skip_cleanup: true
         on:
           repo: httplib2/httplib2
diff --git a/CHANGELOG b/CHANGELOG
index fc9ef77..10e2e4b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,22 @@
+0.14.0
+
+  Python3: PROXY_TYPE_SOCKS5 with str user/pass raised TypeError
+  https://github.com/httplib2/httplib2/pull/145
+
+0.13.1
+
+  Python3: Use no_proxy
+  https://github.com/httplib2/httplib2/pull/140
+
+0.13.0
+
+  Allow setting TLS max/min versions
+  https://github.com/httplib2/httplib2/pull/138
+
+0.12.3
+
+  No changes to library. Distribute py3 wheels.
+
 0.12.1
 
   Catch socket timeouts and clear dead connection
diff --git a/METADATA b/METADATA
index d8f0f8a..72b21cd 100644
--- a/METADATA
+++ b/METADATA
@@ -9,10 +9,10 @@
     type: GIT
     value: "https://github.com/httplib2/httplib2.git"
   }
-  version: "v0.12.1"
+  version: "v0.14.0"
   last_upgrade_date {
     year: 2019
-    month: 2
-    day: 21
+    month: 9
+    day: 26
   }
 }
diff --git a/Makefile b/Makefile
index 96fabaa..3ec958a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,5 @@
+.PHONY: tests
+
 tests:
 	-cd python2 && python2.4 httplib2test.py
 	-cd python2 && python2.5 httplib2test.py
diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py
index fee091d..98228e3 100644
--- a/python2/httplib2/__init__.py
+++ b/python2/httplib2/__init__.py
@@ -19,7 +19,7 @@
     "Alex Yu",
 ]
 __license__ = "MIT"
-__version__ = '0.12.1'
+__version__ = '0.14.0'
 
 import base64
 import calendar
diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py
index 8b64c41..4312f30 100644
--- a/python3/httplib2/__init__.py
+++ b/python3/httplib2/__init__.py
@@ -15,7 +15,7 @@
     "Alex Yu",
 ]
 __license__ = "MIT"
-__version__ = '0.12.1'
+__version__ = '0.14.0'
 
 import base64
 import calendar
@@ -173,9 +173,9 @@
     ssl, "PROTOCOL_SSLv23"
 )
 
-
 def _build_ssl_context(
-    disable_ssl_certificate_validation, ca_certs, cert_file=None, key_file=None
+    disable_ssl_certificate_validation, ca_certs, cert_file=None, key_file=None,
+    maximum_version=None, minimum_version=None,
 ):
     if not hasattr(ssl, "SSLContext"):
         raise RuntimeError("httplib2 requires Python 3.2+ for ssl.SSLContext")
@@ -185,6 +185,19 @@
         ssl.CERT_NONE if disable_ssl_certificate_validation else ssl.CERT_REQUIRED
     )
 
+    # SSLContext.maximum_version and SSLContext.minimum_version are python 3.7+.
+    # source: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.maximum_version
+    if maximum_version is not None:
+        if hasattr(context, "maximum_version"):
+            context.maximum_version = getattr(ssl.TLSVersion, maximum_version)
+        else:
+            raise RuntimeError("setting tls_maximum_version requires Python 3.7 and OpenSSL 1.1 or newer")
+    if minimum_version is not None:
+        if hasattr(context, "minimum_version"):
+            context.minimum_version = getattr(ssl.TLSVersion, minimum_version)
+        else:
+            raise RuntimeError("setting tls_minimum_version requires Python 3.7 and OpenSSL 1.1 or newer")
+
     # check_hostname requires python 3.4+
     # we will perform the equivalent in HTTPSConnectionWithTimeout.connect() by calling ssl.match_hostname
     # if check_hostname is not supported.
@@ -986,6 +999,10 @@
           proxy_headers: Additional or modified headers for the proxy connect
           request.
         """
+        if isinstance(proxy_user, str):
+            proxy_user = proxy_user.encode()
+        if isinstance(proxy_pass, str):
+            proxy_pass = proxy_pass.encode()
         self.proxy_type, self.proxy_host, self.proxy_port, self.proxy_rdns, self.proxy_user, self.proxy_pass, self.proxy_headers = (
             proxy_type,
             proxy_host,
@@ -1123,7 +1140,7 @@
             raise ProxiesUnavailableError(
                 "Proxy support missing but proxy use was requested!"
             )
-        if self.proxy_info and self.proxy_info.isgood():
+        if self.proxy_info and self.proxy_info.isgood() and self.proxy_info.applies_to(self.host):
             use_proxy = True
             proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers = (
                 self.proxy_info.astuple()
@@ -1226,6 +1243,8 @@
         proxy_info=None,
         ca_certs=None,
         disable_ssl_certificate_validation=False,
+        tls_maximum_version=None,
+        tls_minimum_version=None,
     ):
 
         self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
@@ -1236,7 +1255,8 @@
             self.proxy_info = proxy_info("https")
 
         context = _build_ssl_context(
-            self.disable_ssl_certificate_validation, self.ca_certs, cert_file, key_file
+            self.disable_ssl_certificate_validation, self.ca_certs, cert_file, key_file,
+            maximum_version=tls_maximum_version, minimum_version=tls_minimum_version,
         )
         super(HTTPSConnectionWithTimeout, self).__init__(
             host,
@@ -1384,6 +1404,8 @@
         proxy_info=proxy_info_from_environment,
         ca_certs=None,
         disable_ssl_certificate_validation=False,
+        tls_maximum_version=None,
+        tls_minimum_version=None,
     ):
         """If 'cache' is a string then it is used as a directory name for
         a disk cache. Otherwise it must be an object that supports the
@@ -1407,10 +1429,15 @@
 
         If disable_ssl_certificate_validation is true, SSL cert validation will
         not be performed.
+
+        tls_maximum_version / tls_minimum_version require Python 3.7+ /
+        OpenSSL 1.1.0g+. A value of "TLSv1_3" requires OpenSSL 1.1.1+.
 """
         self.proxy_info = proxy_info
         self.ca_certs = ca_certs
         self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
+        self.tls_maximum_version = tls_maximum_version
+        self.tls_minimum_version = tls_minimum_version
         # Map domain name to an httplib connection
         self.connections = {}
         # The location of the cache, for now a directory
@@ -1753,6 +1780,8 @@
                             proxy_info=self.proxy_info,
                             ca_certs=self.ca_certs,
                             disable_ssl_certificate_validation=self.disable_ssl_certificate_validation,
+                            tls_maximum_version=self.tls_maximum_version,
+                            tls_minimum_version=self.tls_minimum_version,
                         )
                     else:
                         conn = self.connections[conn_key] = connection_type(
@@ -1761,6 +1790,8 @@
                             proxy_info=self.proxy_info,
                             ca_certs=self.ca_certs,
                             disable_ssl_certificate_validation=self.disable_ssl_certificate_validation,
+                            tls_maximum_version=self.tls_maximum_version,
+                            tls_minimum_version=self.tls_minimum_version,
                         )
                 else:
                     conn = self.connections[conn_key] = connection_type(
diff --git a/python3/httplib2/socks.py b/python3/httplib2/socks.py
index 24235df..2926b4e 100644
--- a/python3/httplib2/socks.py
+++ b/python3/httplib2/socks.py
@@ -206,13 +206,7 @@
         return "\r\n".join(hdrs)
 
     def __getauthheader(self):
-        username = self.__proxy[4]
-        password = self.__proxy[5]
-        if isinstance(username, str):
-            username = username.encode()
-        if isinstance(password, str):
-            password = password.encode()
-        auth = username + b":" + password
+        auth = self.__proxy[4] + b":" + self.__proxy[5]
         return "Proxy-Authorization: Basic " + base64.b64encode(auth).decode()
 
     def setproxy(
@@ -273,13 +267,13 @@
         elif chosenauth[1:2] == chr(0x02).encode():
             # Okay, we need to perform a basic username/password
             # authentication.
-            self.sendall(
-                chr(0x01).encode()
-                + chr(len(self.__proxy[4]))
-                + self.__proxy[4]
-                + chr(len(self.__proxy[5]))
-                + self.__proxy[5]
-            )
+            packet = bytearray()
+            packet.append(0x01)
+            packet.append(len(self.__proxy[4]))
+            packet.extend(self.__proxy[4])
+            packet.append(len(self.__proxy[5]))
+            packet.extend(self.__proxy[5])
+            self.sendall(packet)
             authstat = self.__recvall(2)
             if authstat[0:1] != chr(0x01).encode():
                 # Bad response
diff --git a/script/compile-py3-openssl11.sh b/script/compile-py3-openssl11.sh
new file mode 100644
index 0000000..3486043
--- /dev/null
+++ b/script/compile-py3-openssl11.sh
@@ -0,0 +1,60 @@
+#!not for running standalone, see .travis.yml
+
+cache_dir=$HOME/.cache
+install_dir=$cache_dir/py3-openssl11
+python_version="3.7.3"
+openssl_version="1.1.1c"
+cpucount=$(nproc --all)
+export PYTHONDONTWRITEBYTECODE=1
+
+#rm -rf $cache_dir/* # uncomment to rebuild
+
+if [[ $($install_dir/bin/python -V) != "Python $python_version" ]] ; then
+  (
+  mkdir -p /tmp/source
+  cd /tmp/source
+  # Compile OpenSSL
+  wget --quiet https://www.openssl.org/source/openssl-$openssl_version.tar.gz
+  echo "Extracting OpenSSL..."
+  tar xf openssl-$openssl_version.tar.gz
+  cd ./openssl-$openssl_version
+  echo "Compiling OpenSSL $openssl_version..."
+  ./config shared --prefix=$install_dir
+  echo "Running make for OpenSSL..."
+  make -j$cpucount -s
+  echo "Running make install for OpenSSL..."
+  make install_sw >/dev/null
+  export LD_LIBRARY_PATH=$install_dir/lib
+
+  cd /tmp/source
+  sudo apt install -qq --yes libffi-dev
+  # Compile latest Python
+  wget --quiet https://www.python.org/ftp/python/$python_version/Python-$python_version.tar.xz
+  echo "Extracting Python..."
+  tar xf Python-$python_version.tar.xz
+  cd ./Python-$python_version
+  echo "Compiling Python $python_version..."
+  # Note we are purposefully NOT using optimization flags as they increase compile time 10x
+  conf_flags="--with-openssl=$install_dir --prefix=$install_dir --with-ensurepip=upgrade"
+  CFLAGS=-O1 ./configure $conf_flags > /dev/null
+  make -j$cpucount -s
+  echo "Installing Python..."
+  make altinstall bininstall >/dev/null
+  ln -fs pip3.7 $install_dir/bin/pip3
+  ln -fs pip3 $install_dir/bin/pip
+  ln -fs python3 $install_dir/bin/python
+  # care for CI cache size
+  find $install_dir -type d -name __pycache__ -print0 |xargs -0 rm -rf
+  )
+fi
+
+export LD_LIBRARY_PATH=$install_dir/lib
+export PATH=$install_dir/bin:$PATH
+if [[ $(python -V) != "Python $python_version" ]] ; then
+  echo "Required Python version was not installed into PATH" >&2
+  exit 1
+fi
+if [[ $(python -c 'import ssl; print(ssl.OPENSSL_VERSION)') != OpenSSL\ ${openssl_version}* ]] ; then
+  echo "Required OpenSSL version was not installed into Python" >&2
+  exit 1
+fi
diff --git a/script/release b/script/release
index 556fc84..0f98e3e 100755
--- a/script/release
+++ b/script/release
@@ -83,18 +83,17 @@
 		confirm "Continue still? [yN] " || exit 1
 	fi
 
-    echo "Building package" >&2
-    find . -name '*.pyc' -o -name '*.pyo' -o -name '*.orig' -delete
-    rm -rf python{2,3}/.cache
-    rm -rf build dist
-    # TODO: sdist bdist_wheel
-    # but wheels don't roll well with our 2/3 split code base
-    local venv=./venv-release
-    if [[ ! -d "$venv" ]] ; then
-        virtualenv $venv
-        $venv/bin/pip install -U pip setuptools wheel twine
-    fi
-    $venv/bin/python setup.py sdist
+	echo "Building package" >&2
+	find . -name '*.pyc' -o -name '*.pyo' -o -name '*.orig' -delete
+	rm -rf python{2,3}/.cache
+	rm -rf build dist
+	local venv=./venv-release
+	if [[ ! -d "$venv" ]] ; then
+		virtualenv $venv
+		$venv/bin/pip install -U pip setuptools wheel twine
+	fi
+	$venv/bin/python setup.py clean --all
+	$venv/bin/python setup.py sdist bdist_wheel
 
 	if confirm "Upload to PyPI? Use in special situation, normally CI (Travis) will upload to PyPI. [yN] " ; then
 		$venv/bin/twine upload dist/* || exit 1
diff --git a/setup.cfg b/setup.cfg
index 432d065..5ebcf6b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,8 @@
+# [bdist_wheel]
+# universal = 1
+# FIXME universal wheels don't roll well with our 2/3 split code base
+# https://github.com/httplib2/httplib2/pull/29
+
 [coverage:run]
 omit = */test/*
 
diff --git a/setup.py b/setup.py
index 7001022..db1db61 100755
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@
 import sys
 
 pkgdir = {"": "python%s" % sys.version_info[0]}
-VERSION = '0.12.1'
+VERSION = '0.14.0'
 
 
 # `python setup.py test` uses existing Python environment, no virtualenv, no pip.
diff --git a/tests/__init__.py b/tests/__init__.py
index 69d7d10..28959d3 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -269,7 +269,8 @@
         gcounter[0] += 1
         keep = True
         keep &= gcounter[0] < request_count
-        keep &= request.headers.get("connection", "").lower() != "close"
+        if request is not None:
+            keep &= request.headers.get("connection", "").lower() != "close"
         return keep
 
     def server_socket_thread(srv):
@@ -295,7 +296,7 @@
                 )
         except Exception as e:
             # traceback.print_exc caused IOError: concurrent operation on sys.stderr.close() under setup.py test
-            sys.stderr.write(traceback.format_exc().encode())
+            print(traceback.format_exc(), file=sys.stderr)
             gresult[0] = e
 
     server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
diff --git a/tests/test_external.py b/tests/test_external.py
index 10b0d8b..0628d96 100644
--- a/tests/test_external.py
+++ b/tests/test_external.py
@@ -96,3 +96,34 @@
     # TODO: make explicit test server with SNI validation
     http = httplib2.Http()
     http.request("https://google.com/", method="GET")
+
+
+@pytest.mark.skipif(
+    os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
+    reason="Python 2.7 doesn't support TLS min/max"
+)
+def test_min_tls_version():
+    # skip on Python versions that don't support TLS min
+    if not hasattr(ssl.SSLContext(), 'minimum_version'):
+        return
+    # BadSSL server that supports max TLS 1.1,
+    # forcing 1.2 should always fail
+    http = httplib2.Http(tls_minimum_version="TLSv1_2")
+    with tests.assert_raises(ssl.SSLError):
+        http.request("https://tls-v1-1.badssl.com:1011/")
+
+
+@pytest.mark.skipif(
+    os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
+    reason="Python 2.7 doesn't support TLS min/max"
+)
+def test_max_tls_version():
+    # skip on Python versions that don't support TLS max
+    if not hasattr(ssl.SSLContext(), 'maximum_version'):
+        return
+    # Google supports TLS 1.2+, confirm we can force down to 1.0
+    # this may break whenever Google disables TLSv1
+    http = httplib2.Http(tls_maximum_version="TLSv1")
+    http.request("https://google.com")
+    _, tls_ver, _ = http.connections['https:google.com'].sock.cipher()
+    assert tls_ver == "TLSv1.0"
diff --git a/tests/test_http.py b/tests/test_http.py
index 2bee6ca..9bd9ee0 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -10,9 +10,11 @@
 import pytest
 from six.moves import http_client, urllib
 import socket
+import ssl
 import tests
 
 DUMMY_URL = "http://127.0.0.1:1"
+DUMMY_HTTPS_URL = "https://127.0.0.1:2"
 
 
 def _raise_connection_refused_exception(*args, **kwargs):
@@ -645,3 +647,38 @@
         assert response.status == 200
         assert content == b"content"
         assert response["link"], "link1, link2"
+
+
+@pytest.mark.skipif(
+    os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
+    reason="Python 2.7 doesn't support TLS min/max"
+)
+def test_set_min_tls_version():
+    # Test setting minimum TLS version
+    # We expect failure on Python < 3.7 or OpenSSL < 1.1
+    expect_success = hasattr(ssl.SSLContext(), 'minimum_version')
+    try:
+        http = httplib2.Http(tls_minimum_version="TLSv1_2")
+        http.request(DUMMY_HTTPS_URL)
+    except RuntimeError:
+        assert not expect_success
+    except socket.error:
+        assert expect_success
+
+
+@pytest.mark.skipif(
+    os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
+    reason="Python 2.7 doesn't support TLS min/max"
+)
+def test_set_max_tls_version():
+    # Test setting maximum TLS version
+    # We expect RuntimeError on Python < 3.7 or OpenSSL < 1.1
+    # We expect socket error otherwise
+    expect_success = hasattr(ssl.SSLContext(), 'maximum_version')
+    try:
+        http = httplib2.Http(tls_maximum_version="TLSv1_2")
+        http.request(DUMMY_HTTPS_URL)
+    except RuntimeError:
+        assert not expect_success
+    except socket.error:
+        assert expect_success
diff --git a/tests/test_proxy.py b/tests/test_proxy.py
index 56a7d99..375367f 100644
--- a/tests/test_proxy.py
+++ b/tests/test_proxy.py
@@ -32,8 +32,8 @@
     pi = httplib2.proxy_info_from_url("http://zoidberg:fish@someproxy:99")
     assert pi.proxy_host == "someproxy"
     assert pi.proxy_port == 99
-    assert pi.proxy_user == "zoidberg"
-    assert pi.proxy_pass == "fish"
+    assert pi.proxy_user == b"zoidberg"
+    assert pi.proxy_pass == b"fish"
 
 
 def test_from_env():
@@ -146,3 +146,28 @@
         )
         response, _ = http.request(uri, "GET")
         assert response.status == 200
+
+
+def test_socks5_auth():
+    def proxy_conn(client, tick):
+        data = client.recv(64)
+        assert data == b"\x05\x02\x00\x02"
+        client.send(b"\x05\x02")  # select username/password auth
+        data = client.recv(64)
+        assert data == b"\x01\x08user_str\x08pass_str"
+        client.send(b"\x01\x01")  # deny
+        tick(None)
+
+    with tests.server_socket(proxy_conn) as uri:
+        uri_parsed = urllib.parse.urlparse(uri)
+        proxy_info = httplib2.ProxyInfo(
+            httplib2.socks.PROXY_TYPE_SOCKS5,
+            proxy_host=uri_parsed.hostname,
+            proxy_port=uri_parsed.port,
+            proxy_rdns=True,
+            proxy_user=u"user_str",
+            proxy_pass=u"pass_str",
+        )
+        http = httplib2.Http(proxy_info=proxy_info)
+        with tests.assert_raises(httplib2.socks.Socks5AuthError):
+            http.request(uri, "GET")