aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2019-10-01 21:50:26 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-10-01 21:50:26 -0700
commitad6153f520dcca2e47173a870f3ae82f51f2d187 (patch)
tree3ed7928f67b78d15fde5300b5e2f203abe05f2ee
parent534a227325dc3dff5ea339cf464034ff1840f848 (diff)
parentb651919b56ba655ef4845bfaddcb3bb37871a71a (diff)
downloadplatform_external_python_httplib2-ad6153f520dcca2e47173a870f3ae82f51f2d187.tar.gz
platform_external_python_httplib2-ad6153f520dcca2e47173a870f3ae82f51f2d187.tar.bz2
platform_external_python_httplib2-ad6153f520dcca2e47173a870f3ae82f51f2d187.zip
Upgrade python/httplib2 to v0.14.0
am: b651919b56 Change-Id: I94af1a904279c5c512e981d0b4e703d452fc47db
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml26
-rw-r--r--CHANGELOG19
-rw-r--r--METADATA6
-rw-r--r--Makefile2
-rw-r--r--python2/httplib2/__init__.py2
-rw-r--r--python3/httplib2/__init__.py41
-rw-r--r--python3/httplib2/socks.py22
-rw-r--r--script/compile-py3-openssl11.sh60
-rwxr-xr-xscript/release23
-rw-r--r--setup.cfg5
-rwxr-xr-xsetup.py2
-rw-r--r--tests/__init__.py5
-rw-r--r--tests/test_external.py31
-rw-r--r--tests/test_http.py37
-rw-r--r--tests/test_proxy.py29
16 files changed, 260 insertions, 51 deletions
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 @@ env:
- 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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ third_party {
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 @@ __contributors__ = [
"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 @@ __contributors__ = [
"Alex Yu",
]
__license__ = "MIT"
-__version__ = '0.12.1'
+__version__ = '0.14.0'
import base64
import calendar
@@ -173,9 +173,9 @@ DEFAULT_TLS_VERSION = getattr(ssl, "PROTOCOL_TLS", None) or getattr(
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 @@ def _build_ssl_context(
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 @@ class ProxyInfo(object):
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 @@ class HTTPConnectionWithTimeout(http.client.HTTPConnection):
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 @@ class HTTPSConnectionWithTimeout(http.client.HTTPSConnection):
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 @@ class HTTPSConnectionWithTimeout(http.client.HTTPSConnection):
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 @@ class Http(object):
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 @@ class Http(object):
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 @@ a string that contains the response entity body.
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 @@ a string that contains the response entity body.
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 @@ class socksocket(socket.socket):
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 @@ class socksocket(socket.socket):
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 @@ interactive() {
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 setuptools.command.test
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 @@ def server_socket(fun, request_count=1, timeout=5):
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 @@ def server_socket(fun, request_count=1, timeout=5):
)
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 @@ def test_sni_hostname_validation():
# 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 os
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 @@ content"""
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 @@ def test_from_url_ident():
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 @@ def test_auth_str_bytes():
)
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")