You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
7.1 KiB
204 lines
7.1 KiB
import httplib2
|
|
import pytest
|
|
from six.moves import urllib
|
|
import socket
|
|
import ssl
|
|
import tests
|
|
|
|
|
|
def test_get_via_https():
|
|
# Test that we can handle HTTPS
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS)
|
|
with tests.server_const_http(tls=True) as uri:
|
|
response, _ = http.request(uri, "GET")
|
|
assert response.status == 200
|
|
|
|
|
|
def test_get_301_via_https():
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS)
|
|
glocation = [""] # nonlocal kind of trick, maybe redundant
|
|
|
|
def handler(request):
|
|
if request.uri == "/final":
|
|
return tests.http_response_bytes(body=b"final")
|
|
return tests.http_response_bytes(status="301 goto", headers={"location": glocation[0]})
|
|
|
|
with tests.server_request(handler, request_count=2, tls=True) as uri:
|
|
glocation[0] = urllib.parse.urljoin(uri, "/final")
|
|
response, content = http.request(uri, "GET")
|
|
assert response.status == 200
|
|
assert content == b"final"
|
|
assert response.previous.status == 301
|
|
assert response.previous["location"] == glocation[0]
|
|
|
|
|
|
def test_get_301_via_https_spec_violation_on_location():
|
|
# Test that we follow redirects through HTTPS
|
|
# even if they violate the spec by including
|
|
# a relative Location: header instead of an absolute one.
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS)
|
|
|
|
def handler(request):
|
|
if request.uri == "/final":
|
|
return tests.http_response_bytes(body=b"final")
|
|
return tests.http_response_bytes(status="301 goto", headers={"location": "/final"})
|
|
|
|
with tests.server_request(handler, request_count=2, tls=True) as uri:
|
|
response, content = http.request(uri, "GET")
|
|
assert response.status == 200
|
|
assert content == b"final"
|
|
assert response.previous.status == 301
|
|
|
|
|
|
def test_invalid_ca_certs_path():
|
|
http = httplib2.Http(ca_certs="/nosuchfile")
|
|
with tests.server_const_http(request_count=0, tls=True) as uri:
|
|
with tests.assert_raises(IOError):
|
|
http.request(uri, "GET")
|
|
|
|
|
|
def test_not_trusted_ca():
|
|
# Test that we get a SSLHandshakeError if we try to access
|
|
# server using a CA cert file that doesn't contain server's CA.
|
|
http = httplib2.Http(ca_certs=tests.CA_UNUSED_CERTS)
|
|
with tests.server_const_http(tls=True) as uri:
|
|
try:
|
|
http.request(uri, "GET")
|
|
assert False, "expected CERTIFICATE_VERIFY_FAILED"
|
|
except ssl.SSLError as e:
|
|
assert e.reason == "CERTIFICATE_VERIFY_FAILED"
|
|
except httplib2.SSLHandshakeError: # Python2
|
|
pass
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not hasattr(tests.ssl_context(), "minimum_version"),
|
|
reason="ssl 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(tests.DUMMY_HTTPS_URL)
|
|
except RuntimeError:
|
|
assert not expect_success
|
|
except socket.error:
|
|
assert expect_success
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not hasattr(tests.ssl_context(), "maximum_version"),
|
|
reason="ssl 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(tests.DUMMY_HTTPS_URL)
|
|
except RuntimeError:
|
|
assert not expect_success
|
|
except socket.error:
|
|
assert expect_success
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not hasattr(tests.ssl_context(), "minimum_version"),
|
|
reason="ssl doesn't support TLS min/max",
|
|
)
|
|
def test_min_tls_version():
|
|
def setup_tls(context, server, skip_errors):
|
|
skip_errors.append("WRONG_VERSION_NUMBER")
|
|
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1)
|
|
context.load_cert_chain(tests.SERVER_CHAIN)
|
|
return context.wrap_socket(server, server_side=True)
|
|
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS, tls_minimum_version="TLSv1_2")
|
|
with tests.server_const_http(tls=setup_tls) as uri:
|
|
try:
|
|
http.request(uri)
|
|
assert False, "expected SSLError"
|
|
except ssl.SSLError as e:
|
|
assert e.reason in ("UNSUPPORTED_PROTOCOL", "VERSION_TOO_LOW")
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not hasattr(tests.ssl_context(), "maximum_version"),
|
|
reason="ssl doesn't support TLS min/max",
|
|
)
|
|
def test_max_tls_version():
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS, tls_maximum_version="TLSv1")
|
|
with tests.server_const_http(tls=True) as uri:
|
|
http.request(uri)
|
|
_, tls_ver, _ = http.connections.popitem()[1].sock.cipher()
|
|
assert tls_ver == "TLSv1.0"
|
|
|
|
|
|
def test_client_cert_verified():
|
|
cert_log = []
|
|
|
|
def setup_tls(context, server, skip_errors):
|
|
context.load_verify_locations(cafile=tests.CA_CERTS)
|
|
context.verify_mode = ssl.CERT_REQUIRED
|
|
return context.wrap_socket(server, server_side=True)
|
|
|
|
def handler(request):
|
|
cert_log.append(request.client_sock.getpeercert())
|
|
return tests.http_response_bytes()
|
|
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS)
|
|
with tests.server_request(handler, tls=setup_tls) as uri:
|
|
uri_parsed = urllib.parse.urlparse(uri)
|
|
http.add_certificate(tests.CLIENT_PEM, tests.CLIENT_PEM, uri_parsed.netloc)
|
|
http.request(uri)
|
|
|
|
assert len(cert_log) == 1
|
|
# TODO extract serial from tests.CLIENT_PEM
|
|
assert cert_log[0]["serialNumber"] == "E2AA6A96D1BF1AEC"
|
|
|
|
|
|
def test_client_cert_password_verified():
|
|
cert_log = []
|
|
|
|
def setup_tls(context, server, skip_errors):
|
|
context.load_verify_locations(cafile=tests.CA_CERTS)
|
|
context.verify_mode = ssl.CERT_REQUIRED
|
|
return context.wrap_socket(server, server_side=True)
|
|
|
|
def handler(request):
|
|
cert_log.append(request.client_sock.getpeercert())
|
|
return tests.http_response_bytes()
|
|
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS)
|
|
with tests.server_request(handler, tls=setup_tls) as uri:
|
|
uri_parsed = urllib.parse.urlparse(uri)
|
|
http.add_certificate(tests.CLIENT_ENCRYPTED_PEM, tests.CLIENT_ENCRYPTED_PEM,
|
|
uri_parsed.netloc, password="12345")
|
|
http.request(uri)
|
|
|
|
assert len(cert_log) == 1
|
|
# TODO extract serial from tests.CLIENT_PEM
|
|
assert cert_log[0]["serialNumber"] == "E2AA6A96D1BF1AED"
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not hasattr(tests.ssl_context(), "set_servername_callback"),
|
|
reason="SSLContext.set_servername_callback is not available",
|
|
)
|
|
def test_sni_set_servername_callback():
|
|
sni_log = []
|
|
|
|
def setup_tls(context, server, skip_errors):
|
|
context.set_servername_callback(lambda _sock, hostname, _context: sni_log.append(hostname))
|
|
return context.wrap_socket(server, server_side=True)
|
|
|
|
http = httplib2.Http(ca_certs=tests.CA_CERTS)
|
|
with tests.server_const_http(tls=setup_tls) as uri:
|
|
uri_parsed = urllib.parse.urlparse(uri)
|
|
http.request(uri)
|
|
assert sni_log == [uri_parsed.hostname]
|