Snap for 6183524 from ad3833c4618fce5cf7f726dc9b23b1556e516376 to r-keystone-qcom-release

Change-Id: I8a0c68447e2f53d2f16cd016b2ebd92c85eca2df
diff --git a/CHANGELOG b/CHANGELOG
index 10e2e4b..07fb949 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,14 @@
+0.15.0
+
+  python2: regression in connect() error handling
+  https://github.com/httplib2/httplib2/pull/150
+
+  add support for password protected certificate files
+  https://github.com/httplib2/httplib2/pull/143
+
+  feature: Http.close() to clean persistent connections and sensitive data
+  https://github.com/httplib2/httplib2/pull/149
+
 0.14.0
 
   Python3: PROXY_TYPE_SOCKS5 with str user/pass raised TypeError
diff --git a/METADATA b/METADATA
index 72b21cd..8b3c1c6 100644
--- a/METADATA
+++ b/METADATA
@@ -9,10 +9,10 @@
     type: GIT
     value: "https://github.com/httplib2/httplib2.git"
   }
-  version: "v0.14.0"
+  version: "v0.15.0"
   last_upgrade_date {
     year: 2019
-    month: 9
-    day: 26
+    month: 12
+    day: 23
   }
 }
diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py
index 98228e3..c8302eb 100644
--- a/python2/httplib2/__init__.py
+++ b/python2/httplib2/__init__.py
@@ -19,7 +19,7 @@
     "Alex Yu",
 ]
 __license__ = "MIT"
-__version__ = '0.14.0'
+__version__ = '0.15.0'
 
 import base64
 import calendar
@@ -76,7 +76,7 @@
 
 
 def _ssl_wrap_socket(
-    sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname
+    sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname, key_password
 ):
     if disable_validation:
         cert_reqs = ssl.CERT_NONE
@@ -90,11 +90,16 @@
         context.verify_mode = cert_reqs
         context.check_hostname = cert_reqs != ssl.CERT_NONE
         if cert_file:
-            context.load_cert_chain(cert_file, key_file)
+            if key_password:
+                context.load_cert_chain(cert_file, key_file, key_password)
+            else:
+                context.load_cert_chain(cert_file, key_file)
         if ca_certs:
             context.load_verify_locations(ca_certs)
         return context.wrap_socket(sock, server_hostname=hostname)
     else:
+        if key_password:
+            raise NotSupportedOnThisPlatform("Certificate with password is not supported.")
         return ssl.wrap_socket(
             sock,
             keyfile=key_file,
@@ -106,7 +111,7 @@
 
 
 def _ssl_wrap_socket_unsupported(
-    sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname
+    sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname, key_password
 ):
     if not disable_validation:
         raise CertificateValidationUnsupported(
@@ -114,6 +119,8 @@
             "the ssl module installed. To avoid this error, install "
             "the ssl module, or explicity disable validation."
         )
+    if key_password:
+        raise NotSupportedOnThisPlatform("Certificate with password is not supported.")
     ssl_sock = socket.ssl(sock, key_file, cert_file)
     return httplib.FakeSocket(sock, ssl_sock)
 
@@ -978,8 +985,13 @@
 class KeyCerts(Credentials):
     """Identical to Credentials except that
     name/password are mapped to key/cert."""
+    def add(self, key, cert, domain, password):
+        self.credentials.append((domain.lower(), key, cert, password))
 
-    pass
+    def iter(self, domain):
+        for (cdomain, key, cert, password) in self.credentials:
+            if cdomain == "" or domain == cdomain:
+                yield (key, cert, password)
 
 
 class AllHosts(object):
@@ -1150,7 +1162,6 @@
             raise ProxiesUnavailableError(
                 "Proxy support missing but proxy use was requested!"
             )
-        msg = "getaddrinfo returns an empty list"
         if self.proxy_info and self.proxy_info.isgood():
             use_proxy = True
             proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers = (
@@ -1164,7 +1175,9 @@
 
             host = self.host
             port = self.port
-
+        
+        socket_err = None
+        
         for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
             af, socktype, proto, canonname, sa = res
             try:
@@ -1206,7 +1219,8 @@
                     self.sock.connect((self.host, self.port) + sa[2:])
                 else:
                     self.sock.connect(sa)
-            except socket.error as msg:
+            except socket.error as e:
+                socket_err = e
                 if self.debuglevel > 0:
                     print("connect fail: (%s, %s)" % (self.host, self.port))
                     if use_proxy:
@@ -1229,7 +1243,7 @@
                 continue
             break
         if not self.sock:
-            raise socket.error(msg)
+            raise socket_err or socket.error("getaddrinfo returns an empty list")
 
 
 class HTTPSConnectionWithTimeout(httplib.HTTPSConnection):
@@ -1253,10 +1267,19 @@
         ca_certs=None,
         disable_ssl_certificate_validation=False,
         ssl_version=None,
+        key_password=None,
     ):
-        httplib.HTTPSConnection.__init__(
-            self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict
-        )
+        if key_password:
+            httplib.HTTPSConnection.__init__(self, host, port=port, strict=strict)
+            self._context.load_cert_chain(cert_file, key_file, key_password)
+            self.key_file = key_file
+            self.cert_file = cert_file
+            self.key_password = key_password
+        else:
+            httplib.HTTPSConnection.__init__(
+                self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict
+            )
+            self.key_password = None
         self.timeout = timeout
         self.proxy_info = proxy_info
         if ca_certs is None:
@@ -1317,7 +1340,6 @@
     def connect(self):
         "Connect to a host on a given (SSL) port."
 
-        msg = "getaddrinfo returns an empty list"
         if self.proxy_info and self.proxy_info.isgood():
             use_proxy = True
             proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers = (
@@ -1331,7 +1353,9 @@
 
             host = self.host
             port = self.port
-
+            
+        socket_err = None
+        
         address_info = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
         for family, socktype, proto, canonname, sockaddr in address_info:
             try:
@@ -1366,6 +1390,7 @@
                     self.ca_certs,
                     self.ssl_version,
                     self.host,
+                    self.key_password,
                 )
                 if self.debuglevel > 0:
                     print("connect: (%s, %s)" % (self.host, self.port))
@@ -1413,7 +1438,8 @@
                     raise
             except (socket.timeout, socket.gaierror):
                 raise
-            except socket.error as msg:
+            except socket.error as e:
+                socket_err = e
                 if self.debuglevel > 0:
                     print("connect fail: (%s, %s)" % (self.host, self.port))
                     if use_proxy:
@@ -1436,7 +1462,7 @@
                 continue
             break
         if not self.sock:
-            raise socket.error(msg)
+            raise socket_err or socket.error("getaddrinfo returns an empty list")
 
 
 SCHEME_TO_CONNECTION = {
@@ -1515,7 +1541,10 @@
         ca_certs=None,
         disable_ssl_certificate_validation=False,
         ssl_version=None,
+        key_password=None,
     ):
+        if key_password:
+            raise NotSupportedOnThisPlatform("Certificate with password is not supported.")
         httplib.HTTPSConnection.__init__(
             self,
             host,
@@ -1649,6 +1678,16 @@
         # Keep Authorization: headers on a redirect.
         self.forward_authorization_headers = False
 
+    def close(self):
+        """Close persistent connections, clear sensitive data.
+        Not thread-safe, requires external synchronization against concurrent requests.
+        """
+        existing, self.connections = self.connections, {}
+        for _, c in existing.iteritems():
+            c.close()
+        self.certificates.clear()
+        self.clear_credentials()
+
     def __getstate__(self):
         state_dict = copy.copy(self.__dict__)
         # In case request is augmented by some foreign object such as
@@ -1680,10 +1719,10 @@
         any time a request requires authentication."""
         self.credentials.add(name, password, domain)
 
-    def add_certificate(self, key, cert, domain):
+    def add_certificate(self, key, cert, domain, password=None):
         """Add a key and cert that will be used
         any time a request requires authentication."""
-        self.certificates.add(key, cert, domain)
+        self.certificates.add(key, cert, domain, password)
 
     def clear_credentials(self):
         """Remove all the names and passwords
@@ -1925,7 +1964,7 @@
         a string that contains the response entity body.
         """
         conn_key = ''
-        
+
         try:
             if headers is None:
                 headers = {}
@@ -1958,6 +1997,7 @@
                             ca_certs=self.ca_certs,
                             disable_ssl_certificate_validation=self.disable_ssl_certificate_validation,
                             ssl_version=self.ssl_version,
+                            key_password=certs[0][2],
                         )
                     else:
                         conn = self.connections[conn_key] = connection_type(
@@ -2140,7 +2180,7 @@
                 conn = self.connections.pop(conn_key, None)
                 if conn:
                     conn.close()
-                    
+
             if self.force_exception_to_status_code:
                 if isinstance(e, HttpLib2ErrorWithResponse):
                     response = e.response
diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py
index 4312f30..d8c3d34 100644
--- a/python3/httplib2/__init__.py
+++ b/python3/httplib2/__init__.py
@@ -15,7 +15,7 @@
     "Alex Yu",
 ]
 __license__ = "MIT"
-__version__ = '0.14.0'
+__version__ = '0.15.0'
 
 import base64
 import calendar
@@ -175,7 +175,7 @@
 
 def _build_ssl_context(
     disable_ssl_certificate_validation, ca_certs, cert_file=None, key_file=None,
-    maximum_version=None, minimum_version=None,
+    maximum_version=None, minimum_version=None, key_password=None,
 ):
     if not hasattr(ssl, "SSLContext"):
         raise RuntimeError("httplib2 requires Python 3.2+ for ssl.SSLContext")
@@ -207,7 +207,7 @@
     context.load_verify_locations(ca_certs)
 
     if cert_file:
-        context.load_cert_chain(cert_file, key_file)
+        context.load_cert_chain(cert_file, key_file, key_password)
 
     return context
 
@@ -959,8 +959,13 @@
 class KeyCerts(Credentials):
     """Identical to Credentials except that
     name/password are mapped to key/cert."""
+    def add(self, key, cert, domain, password):
+        self.credentials.append((domain.lower(), key, cert, password))
 
-    pass
+    def iter(self, domain):
+        for (cdomain, key, cert, password) in self.credentials:
+            if cdomain == "" or domain == cdomain:
+                yield (key, cert, password)
 
 
 class AllHosts(object):
@@ -1245,6 +1250,7 @@
         disable_ssl_certificate_validation=False,
         tls_maximum_version=None,
         tls_minimum_version=None,
+        key_password=None,
     ):
 
         self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
@@ -1257,15 +1263,17 @@
         context = _build_ssl_context(
             self.disable_ssl_certificate_validation, self.ca_certs, cert_file, key_file,
             maximum_version=tls_maximum_version, minimum_version=tls_minimum_version,
+            key_password=key_password,
         )
         super(HTTPSConnectionWithTimeout, self).__init__(
             host,
             port=port,
-            key_file=key_file,
-            cert_file=cert_file,
             timeout=timeout,
             context=context,
         )
+        self.key_file = key_file
+        self.cert_file = cert_file
+        self.key_password = key_password
 
     def connect(self):
         """Connect to a host on a given (SSL) port."""
@@ -1476,6 +1484,16 @@
         # Keep Authorization: headers on a redirect.
         self.forward_authorization_headers = False
 
+    def close(self):
+        """Close persistent connections, clear sensitive data.
+        Not thread-safe, requires external synchronization against concurrent requests.
+        """
+        existing, self.connections = self.connections, {}
+        for _, c in existing.items():
+            c.close()
+        self.certificates.clear()
+        self.clear_credentials()
+
     def __getstate__(self):
         state_dict = copy.copy(self.__dict__)
         # In case request is augmented by some foreign object such as
@@ -1507,10 +1525,10 @@
         any time a request requires authentication."""
         self.credentials.add(name, password, domain)
 
-    def add_certificate(self, key, cert, domain):
+    def add_certificate(self, key, cert, domain, password=None):
         """Add a key and cert that will be used
         any time a request requires authentication."""
-        self.certificates.add(key, cert, domain)
+        self.certificates.add(key, cert, domain, password)
 
     def clear_credentials(self):
         """Remove all the names and passwords
@@ -1782,6 +1800,7 @@
                             disable_ssl_certificate_validation=self.disable_ssl_certificate_validation,
                             tls_maximum_version=self.tls_maximum_version,
                             tls_minimum_version=self.tls_minimum_version,
+                            key_password=certs[0][2],
                         )
                     else:
                         conn = self.connections[conn_key] = connection_type(
diff --git a/script/generate-tls b/script/generate-tls
new file mode 100755
index 0000000..8c96f1e
--- /dev/null
+++ b/script/generate-tls
@@ -0,0 +1,61 @@
+#!/bin/bash
+set -eu
+
+target_dir="${1:-.}"
+days=3650
+rsa_bits=2048
+org="httplib2-test"
+server_cn="localhost"
+subj_prefix="/C=ZZ/ST=./L=./O=$org/OU=."
+
+main() {
+	cd "$target_dir"
+	gen
+	check
+}
+
+check() {
+	echo "- check keys" >&2
+	openssl rsa -in ca.key -check -noout
+	openssl rsa -in client.key -check -noout
+	openssl rsa -in client_encrypted.key -check -noout -passin pass:12345
+	openssl rsa -in server.key -check -noout
+
+	echo "- check certs" >&2
+	for f in *.pem ; do
+		openssl x509 -in "$f" -checkend 3600 -noout
+	done
+}
+
+gen() {
+	echo "- generate keys, if absent" >&2
+	[[ -f ca.key ]] || openssl genrsa -out ca.key $rsa_bits
+	[[ -f client.key ]] || openssl genrsa -out client.key $rsa_bits
+	[[ -f client_encrypted.key ]] || openssl rsa -in client.key -out client_encrypted.key -aes128 -passout pass:12345
+	[[ -f server.key ]] || openssl genrsa -out server.key $rsa_bits
+
+	echo "- generate CA" >&2
+	openssl req -batch -new -nodes -x509 -days $days -subj "$subj_prefix/CN=$org-CA" -key ca.key -out ca.pem
+	openssl req -batch -new -nodes -x509 -days $days -subj "$subj_prefix/CN=$org-CA-unused" -key ca.key -out ca_unused.pem
+
+	echo "- generate client cert" >&2
+	openssl req -batch -new -nodes -out tmp.csr -key client.key -subj "$subj_prefix/CN=$org-client"
+	openssl x509 -req -in tmp.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.crt -days $days -serial -fingerprint
+	cat client.crt client.key >client.pem
+	cat client.crt ca.pem client.key >client_chain.pem
+
+	echo "- generate encrypted client cert" >&2
+	openssl req -batch -new -nodes -out tmp.csr -key client_encrypted.key -passin pass:12345 -subj "$subj_prefix/CN=$org-client-enc"
+	openssl x509 -req -in tmp.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client_encrypted.crt -days $days -serial -fingerprint
+	cat client_encrypted.crt client_encrypted.key >client_encrypted.pem
+
+	echo "- generate server cert" >&2
+	openssl req -batch -new -nodes -out tmp.csr -key server.key -subj "$subj_prefix/CN=$server_cn"
+	openssl x509 -req -in tmp.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.crt -days $days -serial -fingerprint
+	cat server.crt server.key >server.pem
+	cat server.crt ca.pem server.key >server_chain.pem
+
+	rm tmp.csr
+}
+
+main
diff --git a/setup.py b/setup.py
index db1db61..33c8827 100755
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@
 import sys
 
 pkgdir = {"": "python%s" % sys.version_info[0]}
-VERSION = '0.14.0'
+VERSION = '0.15.0'
 
 
 # `python setup.py test` uses existing Python environment, no virtualenv, no pip.
diff --git a/tests/__init__.py b/tests/__init__.py
index 28959d3..496652b 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -14,6 +14,7 @@
 import shutil
 import six
 import socket
+import ssl
 import struct
 import sys
 import threading
@@ -23,6 +24,18 @@
 from six.moves import http_client, queue
 
 
+DUMMY_URL = "http://127.0.0.1:1"
+DUMMY_HTTPS_URL = "https://127.0.0.1:2"
+
+tls_dir = os.path.join(os.path.dirname(__file__), "tls")
+CA_CERTS = os.path.join(tls_dir, "ca.pem")
+CA_UNUSED_CERTS = os.path.join(tls_dir, "ca_unused.pem")
+CLIENT_PEM = os.path.join(tls_dir, "client.pem")
+CLIENT_ENCRYPTED_PEM = os.path.join(tls_dir, "client_encrypted.pem")
+SERVER_PEM = os.path.join(tls_dir, "server.pem")
+SERVER_CHAIN = os.path.join(tls_dir, "server_chain.pem")
+
+
 @contextlib.contextmanager
 def assert_raises(exc_type):
     def _name(t):
@@ -261,9 +274,29 @@
 
 
 @contextlib.contextmanager
-def server_socket(fun, request_count=1, timeout=5):
+def server_socket(fun, request_count=1, timeout=5, scheme="", tls=None):
+    """Base socket server for tests.
+    Likely you want to use server_request or other higher level helpers.
+    All arguments except fun can be passed to other server_* helpers.
+
+    :param fun: fun(client_sock, tick) called after successful accept().
+    :param request_count: test succeeds after exactly this number of requests, triggered by tick(request)
+    :param timeout: seconds.
+    :param scheme: affects yielded value
+        "" - build normal http/https URI.
+        string - build normal URI using supplied scheme.
+        None - yield (addr, port) tuple.
+    :param tls:
+        None (default) - plain HTTP.
+        True - HTTPS with reasonable defaults. Likely you want httplib2.Http(ca_certs=tests.CA_CERTS)
+        string - path to custom server cert+key PEM file.
+        callable - function(context, listener, skip_errors) -> ssl_wrapped_listener
+    """
     gresult = [None]
     gcounter = [0]
+    tls_skip_errors = [
+        "TLSV1_ALERT_UNKNOWN_CA",
+    ]
 
     def tick(request):
         gcounter[0] += 1
@@ -276,7 +309,13 @@
     def server_socket_thread(srv):
         try:
             while gcounter[0] < request_count:
-                client, _ = srv.accept()
+                try:
+                    client, _ = srv.accept()
+                except ssl.SSLError as e:
+                    if e.reason in tls_skip_errors:
+                        return
+                    raise
+
                 try:
                     client.settimeout(timeout)
                     fun(client, tick)
@@ -299,18 +338,36 @@
             print(traceback.format_exc(), file=sys.stderr)
             gresult[0] = e
 
+    bind_hostname = "localhost"
     server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    server.bind(("localhost", 0))
+    server.bind((bind_hostname, 0))
     try:
         server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     except socket.error as ex:
         print("non critical error on SO_REUSEADDR", ex)
     server.listen(10)
     server.settimeout(timeout)
+    server_port = server.getsockname()[1]
+    if tls is True:
+        tls = SERVER_CHAIN
+    if tls:
+        context = ssl_context()
+        if callable(tls):
+            context.load_cert_chain(SERVER_CHAIN)
+            server = tls(context, server, tls_skip_errors)
+        else:
+            context.load_cert_chain(tls)
+            server = context.wrap_socket(server, server_side=True)
+    if scheme == "":
+        scheme = "https" if tls else "http"
+
     t = threading.Thread(target=server_socket_thread, args=(server,))
     t.daemon = True
     t.start()
-    yield u"http://{0}:{1}/".format(*server.getsockname())
+    if scheme is None:
+        yield (bind_hostname, server_port)
+    else:
+        yield u"{scheme}://{host}:{port}/".format(scheme=scheme, host=bind_hostname, port=server_port)
     server.close()
     t.join()
     if gresult[0] is not None:
@@ -329,11 +386,12 @@
             if request is None:
                 break
             i += 1
-            request.client_addr = sock.getsockname()
+            request.client_sock = sock
             request.number = i
             q.put(request)
             response = six.next(g)
             sock.sendall(response)
+            request.client_sock = None
             if not tick(request):
                 break
 
@@ -349,10 +407,11 @@
             if request is None:
                 break
             i += 1
-            request.client_addr = sock.getsockname()
+            request.client_sock = sock
             request.number = i
             response = request_handler(request=request)
             sock.sendall(response)
+            request.client_sock = None
             if not tick(request):
                 break
 
@@ -685,3 +744,11 @@
 
 def deflate_decompress(bs):
     return zlib.decompress(bs, -zlib.MAX_WBITS)
+
+
+def ssl_context(protocol=None):
+    """Workaround for old SSLContext() required protocol argument.
+    """
+    if sys.version_info < (3, 5, 3):
+        return ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+    return ssl.SSLContext()
diff --git a/tests/test_external.py b/tests/test_external.py
deleted file mode 100644
index 0628d96..0000000
--- a/tests/test_external.py
+++ /dev/null
@@ -1,129 +0,0 @@
-"""These tests rely on replies from public internet services
-
-TODO: reimplement with local stubs
-"""
-import httplib2
-import os
-import pytest
-import ssl
-import sys
-import tests
-
-
-def test_get_301_via_https():
-    # Google always redirects to http://google.com
-    http = httplib2.Http()
-    response, content = http.request("https://code.google.com/apis/", "GET")
-    assert response.status == 200
-    assert response.previous.status == 301
-
-
-def test_get_via_https():
-    # Test that we can handle HTTPS
-    http = httplib2.Http()
-    response, content = http.request("https://google.com/adsense/", "GET")
-    assert response.status == 200
-
-
-def test_get_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()
-    response, content = http.request("https://google.com/adsense", "GET")
-    assert response.status == 200
-    assert response.previous is not None
-
-
-def test_get_via_https_key_cert():
-    #  At this point I can only test
-    #  that the key and cert files are passed in
-    #  correctly to httplib. It would be nice to have
-    #  a real https endpoint to test against.
-    http = httplib2.Http(timeout=2)
-    http.add_certificate("akeyfile", "acertfile", "bitworking.org")
-    try:
-        http.request("https://bitworking.org", "GET")
-    except AttributeError:
-        assert http.connections["https:bitworking.org"].key_file == "akeyfile"
-        assert http.connections["https:bitworking.org"].cert_file == "acertfile"
-    except IOError:
-        # Skip on 3.2
-        pass
-
-    try:
-        http.request("https://notthere.bitworking.org", "GET")
-    except httplib2.ServerNotFoundError:
-        assert http.connections["https:notthere.bitworking.org"].key_file is None
-        assert http.connections["https:notthere.bitworking.org"].cert_file is None
-    except IOError:
-        # Skip on 3.2
-        pass
-
-
-def test_ssl_invalid_ca_certs_path():
-    # Test that we get an ssl.SSLError when specifying a non-existent CA
-    # certs file.
-    http = httplib2.Http(ca_certs="/nosuchfile")
-    with tests.assert_raises(IOError):
-        http.request("https://www.google.com/", "GET")
-
-
-@pytest.mark.xfail(
-    sys.version_info <= (3,),
-    reason=(
-        "FIXME: for unknown reason Python 2.7.10 validates www.google.com "
-        "against dummy CA www.example.com"
-    ),
-)
-def test_ssl_wrong_ca():
-    # Test that we get a SSLHandshakeError if we try to access
-    # https://www.google.com, using a CA cert file that doesn't contain
-    # the CA Google uses (i.e., simulating a cert that's not signed by a
-    # trusted CA).
-    other_ca_certs = os.path.join(
-        os.path.dirname(os.path.abspath(httplib2.__file__)), "test", "other_cacerts.txt"
-    )
-    assert os.path.exists(other_ca_certs)
-    http = httplib2.Http(ca_certs=other_ca_certs)
-    http.follow_redirects = False
-    with tests.assert_raises(ssl.SSLError):
-        http.request("https://www.google.com/", "GET")
-
-
-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 9bd9ee0..97b52dc 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -10,12 +10,8 @@
 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):
     raise socket.error(errno.ECONNREFUSED, "Connection refused.")
@@ -25,9 +21,9 @@
     http = httplib2.Http()
     http.force_exception_to_status_code = False
     response, content = http.request(
-        DUMMY_URL, connection_type=tests.MockHTTPConnection
+        tests.DUMMY_URL, connection_type=tests.MockHTTPConnection
     )
-    assert response["content-location"] == DUMMY_URL
+    assert response["content-location"] == tests.DUMMY_URL
     assert content == b"the body"
 
 
@@ -38,7 +34,7 @@
     http.force_exception_to_status_code = False
     try:
         response, content = http.request(
-            DUMMY_URL, connection_type=tests.MockHTTPBadStatusConnection
+            tests.DUMMY_URL, connection_type=tests.MockHTTPBadStatusConnection
         )
     except http_client.BadStatusLine:
         assert tests.MockHTTPBadStatusConnection.num_calls == 2
@@ -71,7 +67,7 @@
     http = httplib2.Http()
     http.force_exception_to_status_code = False
     with tests.assert_raises(socket.error):
-        http.request(DUMMY_URL)
+        http.request(tests.DUMMY_URL)
 
 
 @pytest.mark.skipif(
@@ -84,7 +80,7 @@
     mock_socket_connect.side_effect = _raise_connection_refused_exception
     http = httplib2.Http()
     http.force_exception_to_status_code = True
-    response, content = http.request(DUMMY_URL)
+    response, content = http.request(tests.DUMMY_URL)
     content = content.lower()
     assert response["content-type"] == "text/plain"
     assert (
@@ -647,38 +643,3 @@
         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_https.py b/tests/test_https.py
new file mode 100644
index 0000000..39d7d59
--- /dev/null
+++ b/tests/test_https.py
@@ -0,0 +1,203 @@
+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]
diff --git a/tests/test_other.py b/tests/test_other.py
index f87cfbd..0f450ab 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -232,3 +232,24 @@
         assert len(m.call_args) > 0, "expected Http._request() call"
         conn = m.call_args[0][0]
         assert isinstance(conn, httplib2.HTTPConnectionWithTimeout)
+
+
+def test_close():
+    http = httplib2.Http()
+    assert len(http.connections) == 0
+    with tests.server_const_http() as uri:
+        http.request(uri)
+        assert len(http.connections) == 1
+        http.close()
+        assert len(http.connections) == 0
+
+
+def test_connect_exception_type():
+    # This autoformatting PR actually changed the behavior of error handling:
+    # https://github.com/httplib2/httplib2/pull/105/files#diff-c6669c781a2dee1b2d2671cab4e21c66L985
+    # potentially changing the type of the error raised by connect()
+    # https://github.com/httplib2/httplib2/pull/150
+    http = httplib2.Http()
+    with mock.patch("httplib2.socket.socket.connect", side_effect=socket.timeout("foo")):
+        with tests.assert_raises(socket.timeout):
+            http.request(tests.DUMMY_URL)
diff --git a/tests/tls/ca.key b/tests/tls/ca.key
new file mode 100644
index 0000000..cc1bb1a
--- /dev/null
+++ b/tests/tls/ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrjmlTH
+PFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gHsQn3
+3Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOENh5r
+lYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0QByP
+PACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwCPspC
++wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABAoIBAE+7KqAPmkIeC1Rg
+2/PjtHwUFhwfk/MblIPGm/+38a0c1bT6aJJWbYUS9jhvIZDNQeT8GkrUVKhhnfE0
+Fl4oxPQXGNpJbR0657o11xiZo8QZt5b8cLhGTsY7gFd2jrKBDEgMZ0JsdqCO/m0Q
+pp1KEcelnQBKhHj0UVHnYtVaVo/T0ciS06l0urxpXBW2Bcg5lP/KzBD9md9UJrPU
+9oPM2snMlBTGKH79eTZu4eyKWOMIK+0vSLBnJkAZtNjvitftEs+a0AvVSF+uXbow
+sz7WUpm+BR2wTKikfNMa5nNaLUCdE3sB/1fHQeT7reLEfIVrIfHCyNHi2X0xwWBP
+n9U/Q1ECgYEA/qT9JhIZsy1HjHNliFhfW1lBMLRZdiQwyQeoV0N1aMy68rV3kDqr
+z8qIaHRt2zCgbKgEhB2WRuIL57mXK3WRhk3KP5LEG0wKr7n58iiTtipH/I/04rTG
+RAcYUIR8gDpvd0P1YUJ4dSMqPLDP13+ikA8C3EfL78nxMeCdhyuBA0cCgYEAxR3Q
+Smjkcs8pckl04qOZnnGRpKg/Hmu2wIg2WNisI97t35B0dkZhTOGfsCuMOWJixzEp
+35ZgUzWUd3ACrgZQcxUYlBAdo849QE7lx1Nys8kouPEjbVsRH8Fs1JJkEiVYhoWV
+8JfVzv6Mb95ZBGlvYsiq6p9hT0mLykDNt0xG8o0CgYBOHj1O1ZSuxABEFQ6b0kiG
+lI4MK/eZ56ZTtZauFpLJMK1VUdg5Fdapaz+Hk9gzuuosCys/gHgejLAMSYIXofyf
+z/Nwp0yj9yL8H7iO0mXmJ3hoAZ2lgsGkEu0hnlM3XzXcx6taR/L+NGh7r95DBPPQ
+79n3y8rDaBcnLvoEgpMUdwKBgQCBN6Qdw1lO8gMHiqP3FqxTs7t4J1sJRC9PU3vd
+Dlz6Pt/NGNNf3Y9XaOjYAhQwYhDC57W9fsSyh4NGMMVw8261onS0S0RC56Y7i/0R
+h+C/fvUVF+7Td0loedIwH68+PgEkXloGmGJvCWtiwm20eLGuHkH9AHI4Gcxrz8OL
+j5NK2QKBgCYKtCqB1rXp4k4KcHYQBSmbIN9aEzd4OqjYtzvrYv8wE8ooRim8dd55
+xRRh1r6Nx7+/KoXg7FgaLbDex4dGeCExjvXLmtJNOj0QwJwID84ZhmzjvL26sY8F
+rrt4XPaZv8/5T5kiXE2Mtp3LyIPlq0tRXepdyltrTFPfX3HR+ph2
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/ca.pem b/tests/tls/ca.pem
new file mode 100644
index 0000000..1c05e64
--- /dev/null
+++ b/tests/tls/ca.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRDCCAiwCCQC5E5PSm8flUjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGQxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMRkwFwYDVQQDDBBodHRwbGliMi10ZXN0LUNBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrj
+mlTHPFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gH
+sQn33Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOE
+Nh5rlYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0
+QByPPACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwC
+PspC+wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQB4b+DWt0An4YoXj7lb/+N7FVr2m5UVyBI+bbEGI/qsql/Ixiaef69M
+jej7n5ucUx8GBql62W0c3/E3qZFfo49ngH1WC5gkKQH9V4jGZui5CUfmNE6WepQ/
+vL6eKXUp7RoJ/hWVhGm1uV3OShF+EN0t2wZttYg4lip0FjrY8tRWdjw5yu61wWVu
+WuHxTzKiHe9emjhhUBgnWRnNeYPTRs0xM2Awv5KYPq2cmrjGbSz3mYDkBpbiJUp4
+pM9g8qLmsDO2yrlVF659D08+5zkmMbyqnn84X0n3SM3Yn0ayZOmbNHiXoAzklZNP
+7xiyxMEAfVQOITsvSDG2PzbZlGGtbaka
+-----END CERTIFICATE-----
diff --git a/tests/tls/ca.srl b/tests/tls/ca.srl
new file mode 100644
index 0000000..ad8d416
--- /dev/null
+++ b/tests/tls/ca.srl
@@ -0,0 +1 @@
+E2AA6A96D1BF1AEE
diff --git a/tests/tls/ca_unused.pem b/tests/tls/ca_unused.pem
new file mode 100644
index 0000000..4c4291a
--- /dev/null
+++ b/tests/tls/ca_unused.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUjCCAjoCCQC47jeQyttLgzANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEgMB4GA1UEAwwXaHR0cGxpYjItdGVzdC1DQS11bnVzZWQw
+HhcNMTkwOTI2MTUwMzM0WhcNMjkwOTIzMTUwMzM0WjBrMQswCQYDVQQGEwJaWjEK
+MAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVzdDEK
+MAgGA1UECwwBLjEgMB4GA1UEAwwXaHR0cGxpYjItdGVzdC1DQS11bnVzZWQwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEEp6vCvQzfbfTFYoy/EHbT4BH
+1KiNtAg3b/wQj2ltKuOaVMc8VdZ/TarYuXQY9tydIo4ycABGm1UKTER+QMY83TdT
+HSOMa7YyvBkdRFczaAexCffcaPccKt4UszEiowV8Z4H1PRXG6uBR6T/5tHzlAFU0
+T0atB+sA3w75Jw6kI4Q2HmuVhz9Cxwf9LRAm+21sYBkU/VsH9eOW3B3p8XetmAJ9
+MnzwHZQhwRW35f+Rz/RAHI88AICcJoThwHar5IlUQxg/oEsNW8I9E7ndQLxyqwt+
+kpTPve3Cj82jg+EwfAI+ykL7AqUAPf4Zaaxq3PStHKybtXQaCEIZ75PkkewbAgMB
+AAEwDQYJKoZIhvcNAQELBQADggEBAFbeSPQgXJxfHc1m8wJ4eSW470gXjHZD82uH
+sZTj6v+UZlYzVUgDt+KEdZpoIP8C0prhez+scB6YcwiwP5iHfH3AB51jVoQvKAFt
+4TNKt9LvOuOzGKk9LmO41xYO6KjAOWuoERdYtBR0h0CyOm756iHwO0bQEELiePfU
+hB7o9SlVg0aMcWtbrGBLGBy6HE0p3Oiq/ny0G8r/gshnHvLku6JOxg0XJGDi3LuG
+ezBF0HFwK56NaB2syDtQRCT7I5yqLBK2AlwhcbZat07vLFPeDyw4Omh6COJ/tQsU
+qIcVJ6kS7VJejjWQD8z5CybYDnmBJJqXW4ixUs8wu0l3miaBdiM=
+-----END CERTIFICATE-----
diff --git a/tests/tls/client.crt b/tests/tls/client.crt
new file mode 100644
index 0000000..9430e27
--- /dev/null
+++ b/tests/tls/client.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSDCCAjACCQDiqmqW0b8a7DANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGgxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMR0wGwYDVQQDDBRodHRwbGliMi10ZXN0LWNsaWVudDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALtXT//Esar+MDk6Gcj3KDLAyTU7jPqUx4S83LNI
+GumuBmOg7oe16SSM//NUFhCHiHr4IzeqDOD2Dq1aM661Ta2EINHvpG+9BEkCWkFr
+q4eh0pfocsvU64dtd26TCk9Q4Qqaj4t4HSYYv1hz8UFh5RNLVjgtbR7OQ37Oumcg
+jRGnelV/ck6u5BvN/fKK7p/W6hPcO9OjJDAmlVNZVPtP2ki6Lv/Q87x34X+1/Qb1
+LILUOG5mdfCTmf2tYh9bXqZmqoidTY4O7/JiPuT0+1056Ja8bDGYSFXPvvqd1aEW
+nGA22MEzd74w9A4tIieCRHlGGOSf0AGsVTmHKRf5bpQjaCsCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAi/X3QjpzPap9IhpHqvgFirsEepruz8lCk+Zo6A/+DP/PocII
+/8jWdIV87RDDkkaVGvWOywZyUNN1RAfrt/jGCW8xgCaSGWRab10QIW8DGhbP6FTz
+7xcBnQzcoc1gggZBcwOjkRuefW2zkgGIJo5XxHlBfo3T9nX4086Py/b+VoAmcIlm
+Y/LNHxtIyDDiOgGK9x7+IqEXQuo/p2z5oFubj/hyNJhXaYU2u7nNMXICYY/eY4vX
+GgZ44lGZ2YR7NwzqM5UHNXr7/VJzgxWwAgyZUT8DdnjkZY4wLt1JJas5n3oldBsA
++og2cMk0oOsiFAwHAwE7St4oFY0ivKDhttf7WQ==
+-----END CERTIFICATE-----
diff --git a/tests/tls/client.key b/tests/tls/client.key
new file mode 100644
index 0000000..9a442b8
--- /dev/null
+++ b/tests/tls/client.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAu1dP/8Sxqv4wOToZyPcoMsDJNTuM+pTHhLzcs0ga6a4GY6Du
+h7XpJIz/81QWEIeIevgjN6oM4PYOrVozrrVNrYQg0e+kb70ESQJaQWurh6HSl+hy
+y9Trh213bpMKT1DhCpqPi3gdJhi/WHPxQWHlE0tWOC1tHs5Dfs66ZyCNEad6VX9y
+Tq7kG8398orun9bqE9w706MkMCaVU1lU+0/aSLou/9DzvHfhf7X9BvUsgtQ4bmZ1
+8JOZ/a1iH1tepmaqiJ1Njg7v8mI+5PT7XTnolrxsMZhIVc+++p3VoRacYDbYwTN3
+vjD0Di0iJ4JEeUYY5J/QAaxVOYcpF/lulCNoKwIDAQABAoIBAQCtzBeUYWauCni0
+bnlDXj91rjI7504nneTm+MsKq6cECQU2YjNHxXRQC1rb47NAjGwKIK+TUXf3L254
+VglCWEKC6eQEvvxfCQyzqrIOpRORlYeok+YDwTjr/5rgAxt6b78GtlLbAYiRMj45
+kf5MOMRqvOZ04XetL4+gUarOR3131L38ysReluabsCXkgIH9kZHmgOW2El4lmoHp
+CQpvMkJWyoVZvDbjLi0JoEljHGpfdWNdcllHP4dbNSQtfgG3VXXFgKqWQ8ZiY2U1
+y5SxHaeAjKHBUoGeinox/Myzan3xCysZb+gi5UxcrE7dn5lSB2/AMBymYShI/2qi
+UWq64JeJAoGBAPLtHRIcbRn53IhXGwcdFAn7JUSzKvr+gUfJiyfbw28CDWVRgTce
+JN+FzTuW92Iwm3ppBKmJ5PcZZnqt7VTtWfLvP126YaGctqZHSWiD9oK3EzDJEWIO
+trpMlJkeB+IQlvYMCiC+G+6XFBCdB7X3X1D9Y9z11Kf/arx24bB/ByVlAoGBAMVs
+YZRL9idgwgU4LMOqaPkU99de4wzYF44joZrp3Eme3dC9sdHrDtDy6OpiQu4zP+Ax
+5cws6M6txd5meAh2YwRhJBmGUYIQuhhNKQjhoeovw0tbtXO9rAHPegXPqg8xwzY9
+Ntc/WlfwM0O7ROfOq4r9erWBn0B7xspxRMH+LIZPAoGBALVW326XnbHYXRHBxEFJ
+KZ5Rxf5EqP74YVVPU/uLB5akN4+8ifK1I91fqlajWUQI+Ocl4f8VGsCCS4ekshfF
+nnHEus6ixSK5M3dom5nTeH8XXtH6JmnGhg0IAZ1TV5sfuzEsx5qtj3hJewbz0b+6
+S4LPxG47bGWEOw84xzzTdmgpAoGAGj87heTHeBrEEL+UK/tW826XOLnzw7xi/VG9
+ZYQb9mm5ocvmfTscACmbT7X6ogKMRnk7zPZXiUrPGK9U3AMpTObBTudtpLYml56C
+ixy8Uw9Ajp9Fs3qPCLqVxXoDaPu7sVVYGivhDfnwRtv54Du40MS8cK8oBgGuvzFp
+68SoFL8CgYAY7KvTfTKk4oWWeclmwEoe04woV7J6XuB7OnbxYpKpiGh4juN1E6wo
+n9UhAVzO6cAfK/ZuhTkDtvJSsXtQ1xElZLMIG1Yb7yikRyO73EHRUpHon3Gah+79
+MM6uZReiEdkx/hMthL45jP85hfVM89M7LYj9SBoxY2xpuzN+HmiezQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/client.pem b/tests/tls/client.pem
new file mode 100644
index 0000000..f12775f
--- /dev/null
+++ b/tests/tls/client.pem
@@ -0,0 +1,47 @@
+-----BEGIN CERTIFICATE-----
+MIIDSDCCAjACCQDiqmqW0b8a7DANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGgxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMR0wGwYDVQQDDBRodHRwbGliMi10ZXN0LWNsaWVudDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALtXT//Esar+MDk6Gcj3KDLAyTU7jPqUx4S83LNI
+GumuBmOg7oe16SSM//NUFhCHiHr4IzeqDOD2Dq1aM661Ta2EINHvpG+9BEkCWkFr
+q4eh0pfocsvU64dtd26TCk9Q4Qqaj4t4HSYYv1hz8UFh5RNLVjgtbR7OQ37Oumcg
+jRGnelV/ck6u5BvN/fKK7p/W6hPcO9OjJDAmlVNZVPtP2ki6Lv/Q87x34X+1/Qb1
+LILUOG5mdfCTmf2tYh9bXqZmqoidTY4O7/JiPuT0+1056Ja8bDGYSFXPvvqd1aEW
+nGA22MEzd74w9A4tIieCRHlGGOSf0AGsVTmHKRf5bpQjaCsCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAi/X3QjpzPap9IhpHqvgFirsEepruz8lCk+Zo6A/+DP/PocII
+/8jWdIV87RDDkkaVGvWOywZyUNN1RAfrt/jGCW8xgCaSGWRab10QIW8DGhbP6FTz
+7xcBnQzcoc1gggZBcwOjkRuefW2zkgGIJo5XxHlBfo3T9nX4086Py/b+VoAmcIlm
+Y/LNHxtIyDDiOgGK9x7+IqEXQuo/p2z5oFubj/hyNJhXaYU2u7nNMXICYY/eY4vX
+GgZ44lGZ2YR7NwzqM5UHNXr7/VJzgxWwAgyZUT8DdnjkZY4wLt1JJas5n3oldBsA
++og2cMk0oOsiFAwHAwE7St4oFY0ivKDhttf7WQ==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAu1dP/8Sxqv4wOToZyPcoMsDJNTuM+pTHhLzcs0ga6a4GY6Du
+h7XpJIz/81QWEIeIevgjN6oM4PYOrVozrrVNrYQg0e+kb70ESQJaQWurh6HSl+hy
+y9Trh213bpMKT1DhCpqPi3gdJhi/WHPxQWHlE0tWOC1tHs5Dfs66ZyCNEad6VX9y
+Tq7kG8398orun9bqE9w706MkMCaVU1lU+0/aSLou/9DzvHfhf7X9BvUsgtQ4bmZ1
+8JOZ/a1iH1tepmaqiJ1Njg7v8mI+5PT7XTnolrxsMZhIVc+++p3VoRacYDbYwTN3
+vjD0Di0iJ4JEeUYY5J/QAaxVOYcpF/lulCNoKwIDAQABAoIBAQCtzBeUYWauCni0
+bnlDXj91rjI7504nneTm+MsKq6cECQU2YjNHxXRQC1rb47NAjGwKIK+TUXf3L254
+VglCWEKC6eQEvvxfCQyzqrIOpRORlYeok+YDwTjr/5rgAxt6b78GtlLbAYiRMj45
+kf5MOMRqvOZ04XetL4+gUarOR3131L38ysReluabsCXkgIH9kZHmgOW2El4lmoHp
+CQpvMkJWyoVZvDbjLi0JoEljHGpfdWNdcllHP4dbNSQtfgG3VXXFgKqWQ8ZiY2U1
+y5SxHaeAjKHBUoGeinox/Myzan3xCysZb+gi5UxcrE7dn5lSB2/AMBymYShI/2qi
+UWq64JeJAoGBAPLtHRIcbRn53IhXGwcdFAn7JUSzKvr+gUfJiyfbw28CDWVRgTce
+JN+FzTuW92Iwm3ppBKmJ5PcZZnqt7VTtWfLvP126YaGctqZHSWiD9oK3EzDJEWIO
+trpMlJkeB+IQlvYMCiC+G+6XFBCdB7X3X1D9Y9z11Kf/arx24bB/ByVlAoGBAMVs
+YZRL9idgwgU4LMOqaPkU99de4wzYF44joZrp3Eme3dC9sdHrDtDy6OpiQu4zP+Ax
+5cws6M6txd5meAh2YwRhJBmGUYIQuhhNKQjhoeovw0tbtXO9rAHPegXPqg8xwzY9
+Ntc/WlfwM0O7ROfOq4r9erWBn0B7xspxRMH+LIZPAoGBALVW326XnbHYXRHBxEFJ
+KZ5Rxf5EqP74YVVPU/uLB5akN4+8ifK1I91fqlajWUQI+Ocl4f8VGsCCS4ekshfF
+nnHEus6ixSK5M3dom5nTeH8XXtH6JmnGhg0IAZ1TV5sfuzEsx5qtj3hJewbz0b+6
+S4LPxG47bGWEOw84xzzTdmgpAoGAGj87heTHeBrEEL+UK/tW826XOLnzw7xi/VG9
+ZYQb9mm5ocvmfTscACmbT7X6ogKMRnk7zPZXiUrPGK9U3AMpTObBTudtpLYml56C
+ixy8Uw9Ajp9Fs3qPCLqVxXoDaPu7sVVYGivhDfnwRtv54Du40MS8cK8oBgGuvzFp
+68SoFL8CgYAY7KvTfTKk4oWWeclmwEoe04woV7J6XuB7OnbxYpKpiGh4juN1E6wo
+n9UhAVzO6cAfK/ZuhTkDtvJSsXtQ1xElZLMIG1Yb7yikRyO73EHRUpHon3Gah+79
+MM6uZReiEdkx/hMthL45jP85hfVM89M7LYj9SBoxY2xpuzN+HmiezQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/client_chain.pem b/tests/tls/client_chain.pem
new file mode 100644
index 0000000..e2427d7
--- /dev/null
+++ b/tests/tls/client_chain.pem
@@ -0,0 +1,67 @@
+-----BEGIN CERTIFICATE-----
+MIIDSDCCAjACCQDiqmqW0b8a7DANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGgxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMR0wGwYDVQQDDBRodHRwbGliMi10ZXN0LWNsaWVudDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALtXT//Esar+MDk6Gcj3KDLAyTU7jPqUx4S83LNI
+GumuBmOg7oe16SSM//NUFhCHiHr4IzeqDOD2Dq1aM661Ta2EINHvpG+9BEkCWkFr
+q4eh0pfocsvU64dtd26TCk9Q4Qqaj4t4HSYYv1hz8UFh5RNLVjgtbR7OQ37Oumcg
+jRGnelV/ck6u5BvN/fKK7p/W6hPcO9OjJDAmlVNZVPtP2ki6Lv/Q87x34X+1/Qb1
+LILUOG5mdfCTmf2tYh9bXqZmqoidTY4O7/JiPuT0+1056Ja8bDGYSFXPvvqd1aEW
+nGA22MEzd74w9A4tIieCRHlGGOSf0AGsVTmHKRf5bpQjaCsCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAi/X3QjpzPap9IhpHqvgFirsEepruz8lCk+Zo6A/+DP/PocII
+/8jWdIV87RDDkkaVGvWOywZyUNN1RAfrt/jGCW8xgCaSGWRab10QIW8DGhbP6FTz
+7xcBnQzcoc1gggZBcwOjkRuefW2zkgGIJo5XxHlBfo3T9nX4086Py/b+VoAmcIlm
+Y/LNHxtIyDDiOgGK9x7+IqEXQuo/p2z5oFubj/hyNJhXaYU2u7nNMXICYY/eY4vX
+GgZ44lGZ2YR7NwzqM5UHNXr7/VJzgxWwAgyZUT8DdnjkZY4wLt1JJas5n3oldBsA
++og2cMk0oOsiFAwHAwE7St4oFY0ivKDhttf7WQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDRDCCAiwCCQC5E5PSm8flUjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGQxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMRkwFwYDVQQDDBBodHRwbGliMi10ZXN0LUNBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrj
+mlTHPFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gH
+sQn33Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOE
+Nh5rlYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0
+QByPPACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwC
+PspC+wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQB4b+DWt0An4YoXj7lb/+N7FVr2m5UVyBI+bbEGI/qsql/Ixiaef69M
+jej7n5ucUx8GBql62W0c3/E3qZFfo49ngH1WC5gkKQH9V4jGZui5CUfmNE6WepQ/
+vL6eKXUp7RoJ/hWVhGm1uV3OShF+EN0t2wZttYg4lip0FjrY8tRWdjw5yu61wWVu
+WuHxTzKiHe9emjhhUBgnWRnNeYPTRs0xM2Awv5KYPq2cmrjGbSz3mYDkBpbiJUp4
+pM9g8qLmsDO2yrlVF659D08+5zkmMbyqnn84X0n3SM3Yn0ayZOmbNHiXoAzklZNP
+7xiyxMEAfVQOITsvSDG2PzbZlGGtbaka
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAu1dP/8Sxqv4wOToZyPcoMsDJNTuM+pTHhLzcs0ga6a4GY6Du
+h7XpJIz/81QWEIeIevgjN6oM4PYOrVozrrVNrYQg0e+kb70ESQJaQWurh6HSl+hy
+y9Trh213bpMKT1DhCpqPi3gdJhi/WHPxQWHlE0tWOC1tHs5Dfs66ZyCNEad6VX9y
+Tq7kG8398orun9bqE9w706MkMCaVU1lU+0/aSLou/9DzvHfhf7X9BvUsgtQ4bmZ1
+8JOZ/a1iH1tepmaqiJ1Njg7v8mI+5PT7XTnolrxsMZhIVc+++p3VoRacYDbYwTN3
+vjD0Di0iJ4JEeUYY5J/QAaxVOYcpF/lulCNoKwIDAQABAoIBAQCtzBeUYWauCni0
+bnlDXj91rjI7504nneTm+MsKq6cECQU2YjNHxXRQC1rb47NAjGwKIK+TUXf3L254
+VglCWEKC6eQEvvxfCQyzqrIOpRORlYeok+YDwTjr/5rgAxt6b78GtlLbAYiRMj45
+kf5MOMRqvOZ04XetL4+gUarOR3131L38ysReluabsCXkgIH9kZHmgOW2El4lmoHp
+CQpvMkJWyoVZvDbjLi0JoEljHGpfdWNdcllHP4dbNSQtfgG3VXXFgKqWQ8ZiY2U1
+y5SxHaeAjKHBUoGeinox/Myzan3xCysZb+gi5UxcrE7dn5lSB2/AMBymYShI/2qi
+UWq64JeJAoGBAPLtHRIcbRn53IhXGwcdFAn7JUSzKvr+gUfJiyfbw28CDWVRgTce
+JN+FzTuW92Iwm3ppBKmJ5PcZZnqt7VTtWfLvP126YaGctqZHSWiD9oK3EzDJEWIO
+trpMlJkeB+IQlvYMCiC+G+6XFBCdB7X3X1D9Y9z11Kf/arx24bB/ByVlAoGBAMVs
+YZRL9idgwgU4LMOqaPkU99de4wzYF44joZrp3Eme3dC9sdHrDtDy6OpiQu4zP+Ax
+5cws6M6txd5meAh2YwRhJBmGUYIQuhhNKQjhoeovw0tbtXO9rAHPegXPqg8xwzY9
+Ntc/WlfwM0O7ROfOq4r9erWBn0B7xspxRMH+LIZPAoGBALVW326XnbHYXRHBxEFJ
+KZ5Rxf5EqP74YVVPU/uLB5akN4+8ifK1I91fqlajWUQI+Ocl4f8VGsCCS4ekshfF
+nnHEus6ixSK5M3dom5nTeH8XXtH6JmnGhg0IAZ1TV5sfuzEsx5qtj3hJewbz0b+6
+S4LPxG47bGWEOw84xzzTdmgpAoGAGj87heTHeBrEEL+UK/tW826XOLnzw7xi/VG9
+ZYQb9mm5ocvmfTscACmbT7X6ogKMRnk7zPZXiUrPGK9U3AMpTObBTudtpLYml56C
+ixy8Uw9Ajp9Fs3qPCLqVxXoDaPu7sVVYGivhDfnwRtv54Du40MS8cK8oBgGuvzFp
+68SoFL8CgYAY7KvTfTKk4oWWeclmwEoe04woV7J6XuB7OnbxYpKpiGh4juN1E6wo
+n9UhAVzO6cAfK/ZuhTkDtvJSsXtQ1xElZLMIG1Yb7yikRyO73EHRUpHon3Gah+79
+MM6uZReiEdkx/hMthL45jP85hfVM89M7LYj9SBoxY2xpuzN+HmiezQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/client_encrypted.crt b/tests/tls/client_encrypted.crt
new file mode 100644
index 0000000..6301dd5
--- /dev/null
+++ b/tests/tls/client_encrypted.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjQCCQDiqmqW0b8a7TANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGwxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMSEwHwYDVQQDDBhodHRwbGliMi10ZXN0LWNsaWVudC1lbmMwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V0//xLGq/jA5OhnI9ygywMk1O4z6lMeE
+vNyzSBrprgZjoO6HtekkjP/zVBYQh4h6+CM3qgzg9g6tWjOutU2thCDR76RvvQRJ
+AlpBa6uHodKX6HLL1OuHbXdukwpPUOEKmo+LeB0mGL9Yc/FBYeUTS1Y4LW0ezkN+
+zrpnII0Rp3pVf3JOruQbzf3yiu6f1uoT3DvToyQwJpVTWVT7T9pIui7/0PO8d+F/
+tf0G9SyC1DhuZnXwk5n9rWIfW16mZqqInU2ODu/yYj7k9PtdOeiWvGwxmEhVz776
+ndWhFpxgNtjBM3e+MPQOLSIngkR5Rhjkn9ABrFU5hykX+W6UI2grAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADggEBAKiBTMX/FwUusM4PIsmGqXisOBo6LEf2YtfzQrtxw4eY
+eWeKsi3aM2GquCqh0R7loEW+yQoxPEBaNeOBeN3v8sdhTu+9NjK31tWCYr7jvEa5
+TqjlUUMD1176YBQ8axI51lVcaBIoRdvf8nXm7idvp82eBBXQtnREjd8oKcEz7v4x
+ECJ+RWGJTEIWXq3fuVvBAJeopNVz+Utt61DCxziKbu+ndv0kQeXZ7KPFiBnARcEi
+7GvTeHUA0cbpHrNY0ob7ozcjGiPwW5HPi+DYZYfRm2PqI9vowmKt9By+8Uz03K3L
+XMZkGJ28uoo37Rbjs8+pMVDdHoUrm6hZTkw5XGgsA6I=
+-----END CERTIFICATE-----
diff --git a/tests/tls/client_encrypted.key b/tests/tls/client_encrypted.key
new file mode 100644
index 0000000..747b1fb
--- /dev/null
+++ b/tests/tls/client_encrypted.key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,84B979083590A865DF5700455C62AEAF
+
+Q2vQ9vhIV6C3aL4Uk9XIqtsgdN9VN+Ep3mDY7FKbycrcZgDnXbZ5ys50cF8TyJZx
+ITYV5ms9MjjUi1cj3hhUfQSu1bJ+gKGWpxHD1Cxub9XLcGJrjfGomO0r5lNe4YZf
+of4qc+XpoY+dZTHyFuKn44LiynAG0esqexNc7BcqbGSw6cjm6exvDWbmz0svy2RO
+TTfea6oh2+CiHUWqIbjIEoK/tJXXzUq9LwEMGdvyQX5KJDLmsWdxejnqsMmSDBH4
+Bm5ax7AIcO4SE1AdYzzDNI11bZXyQu1UWtgL3D4sDo4iwbHzq9CFXSI21tgwW3WT
+LK3Pbt6IAJctwNZozwWDT9c0r226WZv/lYk4bkhvGDuLFMgMwjmW24i4VuU7giiv
+sna6jJNk0gQ0XCWiIgtjhrH8noefj07SPb5miXQjhbCvz3F5TqGobWS6xSCX+KMF
+LfjJdG5f6QHfnzm+fmIuc/JdcHMCf/2q3bvHuTOoYoJXMY9kcKS5PXckADsQkzmM
+2AeoegO8x25ClHRBSOgwee9tkrD5VTX3uK92/rnnt3KUiS8Rdf6jbYpJg2rvCTAd
+NF6GCCp+YNw3o7a5IehhhwXWKh0BK02e6IPU9Z6KFTX8LGFOPy8BIghyNILPpfeC
+Ir2WWr8asfBLvwy5Pj6Wzfvu6EPC6H0RIxzhTTfaZ0KZR3ZN1Fwd49FNql50UeHH
+M5x1vzi4JuRak4z22+uL3GEdkBs/PAoVMfctHTMEiDrH3mgYrEj5yhCgUjJhMP6w
+dk6gUaXcPZ6JymUtrZsjcc3cXDD6Vn6i1SH2e9rMf9/QBKGZH3ufkBJ9s0HVsacq
+bHg63+BGhmLXFcnEimGVY86py8TfQfuirFOEURoYJMUTCl046GV11AJeF0L+EOUH
+nst2Z9l86vCH+7sdVPsVG8jFaeOunr50VkCu/ephGsUSHjNRVagsVOSojknMyNki
+D13Oolykusq9zuHMBBRceNRoAHLSk02aHiuwlvQmAVaSJxIebtFZJmG1asXMSK/M
+iYjLNb7P+fjpvxYwk1bxXTyTToHLsmvDeGyMj3EhrEh83o/Xoo+bac7OW+l18wWw
+N5b5Jcrj0e/O08+dJ5UBXGJ36V5WENTdOjctFTRtZhggkvB1QBZLa1g9khxz0VEw
+q0yGYCmjMK2YxpJhH+FAprUM+0Ei9IxIjDY295CEOrKkeJGb9+A8CWLoCi8z0zAc
+vQU/uaQsK1BKamKGmu7x+xCyoLSprzNlPjipJttUqkE00kFX9sBJZXW/rI+fQRJI
+sYn9UqNp/ay00uswwcoFLAnX7YdKiJrB5jaR5oyLFXpIPQa76594iRcf+9XkD9Ca
+KlscDNVnfSW7bkp7LJ+Rm6+OKWCSjglL8uIyBEzoNFCyiEnnzebE+jwvVBaDlbPd
+xMwMzU8vHsb9dYd7RMdd+YBIxngzyJiVl6Zpfy9B74vhL+ndyWKZg2Rsunlrcms7
+YIVg/LuAfgH4jXg38yzHHEkArGZg5TFaGUD8rJwMIPil6LOQ4D+jK8/fcV9bhBuH
+LzBJ4gtPwUnvYqsaiIAeGi2EVllW0Ka+aTTzM1Yascl2q9WROvutAT0zz0M6smpO
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/client_encrypted.pem b/tests/tls/client_encrypted.pem
new file mode 100644
index 0000000..578aa7f
--- /dev/null
+++ b/tests/tls/client_encrypted.pem
@@ -0,0 +1,50 @@
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjQCCQDiqmqW0b8a7TANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGwxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMSEwHwYDVQQDDBhodHRwbGliMi10ZXN0LWNsaWVudC1lbmMwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V0//xLGq/jA5OhnI9ygywMk1O4z6lMeE
+vNyzSBrprgZjoO6HtekkjP/zVBYQh4h6+CM3qgzg9g6tWjOutU2thCDR76RvvQRJ
+AlpBa6uHodKX6HLL1OuHbXdukwpPUOEKmo+LeB0mGL9Yc/FBYeUTS1Y4LW0ezkN+
+zrpnII0Rp3pVf3JOruQbzf3yiu6f1uoT3DvToyQwJpVTWVT7T9pIui7/0PO8d+F/
+tf0G9SyC1DhuZnXwk5n9rWIfW16mZqqInU2ODu/yYj7k9PtdOeiWvGwxmEhVz776
+ndWhFpxgNtjBM3e+MPQOLSIngkR5Rhjkn9ABrFU5hykX+W6UI2grAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADggEBAKiBTMX/FwUusM4PIsmGqXisOBo6LEf2YtfzQrtxw4eY
+eWeKsi3aM2GquCqh0R7loEW+yQoxPEBaNeOBeN3v8sdhTu+9NjK31tWCYr7jvEa5
+TqjlUUMD1176YBQ8axI51lVcaBIoRdvf8nXm7idvp82eBBXQtnREjd8oKcEz7v4x
+ECJ+RWGJTEIWXq3fuVvBAJeopNVz+Utt61DCxziKbu+ndv0kQeXZ7KPFiBnARcEi
+7GvTeHUA0cbpHrNY0ob7ozcjGiPwW5HPi+DYZYfRm2PqI9vowmKt9By+8Uz03K3L
+XMZkGJ28uoo37Rbjs8+pMVDdHoUrm6hZTkw5XGgsA6I=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,84B979083590A865DF5700455C62AEAF
+
+Q2vQ9vhIV6C3aL4Uk9XIqtsgdN9VN+Ep3mDY7FKbycrcZgDnXbZ5ys50cF8TyJZx
+ITYV5ms9MjjUi1cj3hhUfQSu1bJ+gKGWpxHD1Cxub9XLcGJrjfGomO0r5lNe4YZf
+of4qc+XpoY+dZTHyFuKn44LiynAG0esqexNc7BcqbGSw6cjm6exvDWbmz0svy2RO
+TTfea6oh2+CiHUWqIbjIEoK/tJXXzUq9LwEMGdvyQX5KJDLmsWdxejnqsMmSDBH4
+Bm5ax7AIcO4SE1AdYzzDNI11bZXyQu1UWtgL3D4sDo4iwbHzq9CFXSI21tgwW3WT
+LK3Pbt6IAJctwNZozwWDT9c0r226WZv/lYk4bkhvGDuLFMgMwjmW24i4VuU7giiv
+sna6jJNk0gQ0XCWiIgtjhrH8noefj07SPb5miXQjhbCvz3F5TqGobWS6xSCX+KMF
+LfjJdG5f6QHfnzm+fmIuc/JdcHMCf/2q3bvHuTOoYoJXMY9kcKS5PXckADsQkzmM
+2AeoegO8x25ClHRBSOgwee9tkrD5VTX3uK92/rnnt3KUiS8Rdf6jbYpJg2rvCTAd
+NF6GCCp+YNw3o7a5IehhhwXWKh0BK02e6IPU9Z6KFTX8LGFOPy8BIghyNILPpfeC
+Ir2WWr8asfBLvwy5Pj6Wzfvu6EPC6H0RIxzhTTfaZ0KZR3ZN1Fwd49FNql50UeHH
+M5x1vzi4JuRak4z22+uL3GEdkBs/PAoVMfctHTMEiDrH3mgYrEj5yhCgUjJhMP6w
+dk6gUaXcPZ6JymUtrZsjcc3cXDD6Vn6i1SH2e9rMf9/QBKGZH3ufkBJ9s0HVsacq
+bHg63+BGhmLXFcnEimGVY86py8TfQfuirFOEURoYJMUTCl046GV11AJeF0L+EOUH
+nst2Z9l86vCH+7sdVPsVG8jFaeOunr50VkCu/ephGsUSHjNRVagsVOSojknMyNki
+D13Oolykusq9zuHMBBRceNRoAHLSk02aHiuwlvQmAVaSJxIebtFZJmG1asXMSK/M
+iYjLNb7P+fjpvxYwk1bxXTyTToHLsmvDeGyMj3EhrEh83o/Xoo+bac7OW+l18wWw
+N5b5Jcrj0e/O08+dJ5UBXGJ36V5WENTdOjctFTRtZhggkvB1QBZLa1g9khxz0VEw
+q0yGYCmjMK2YxpJhH+FAprUM+0Ei9IxIjDY295CEOrKkeJGb9+A8CWLoCi8z0zAc
+vQU/uaQsK1BKamKGmu7x+xCyoLSprzNlPjipJttUqkE00kFX9sBJZXW/rI+fQRJI
+sYn9UqNp/ay00uswwcoFLAnX7YdKiJrB5jaR5oyLFXpIPQa76594iRcf+9XkD9Ca
+KlscDNVnfSW7bkp7LJ+Rm6+OKWCSjglL8uIyBEzoNFCyiEnnzebE+jwvVBaDlbPd
+xMwMzU8vHsb9dYd7RMdd+YBIxngzyJiVl6Zpfy9B74vhL+ndyWKZg2Rsunlrcms7
+YIVg/LuAfgH4jXg38yzHHEkArGZg5TFaGUD8rJwMIPil6LOQ4D+jK8/fcV9bhBuH
+LzBJ4gtPwUnvYqsaiIAeGi2EVllW0Ka+aTTzM1Yascl2q9WROvutAT0zz0M6smpO
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/server.crt b/tests/tls/server.crt
new file mode 100644
index 0000000..e29537a
--- /dev/null
+++ b/tests/tls/server.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPTCCAiUCCQDiqmqW0b8a7jANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMF0xCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCu9WJEGBARCdyqt+/ip53p+S7N0lKGv0w75LfqVldIQGYMAf9xSMcb
+PSTds9rYq8g7XaehNJ628w0hWnHDEU1x28ULMlL22p91inEmWANyi+flCopgUgPe
+sQkc+kcXUVv+/Of8eclIXCxni67J8fK7nH/48ML+qdWes9nrtMRB5z/2mC7sc53x
+4faJdOpCx13CaHvYPIHJfT7d7haTDBGLVCSC90tB5WvK7E1hIoIddqwrthWopRcS
+iSmQfJZmKPgPTtnVX1r3meLEVcdLcZ9OO8r1buUrBdB5Z25K5r0nCYzAk4AyJ1pq
+8tjajMwsunbLFOQ6X9DNRCVOG0XDpu0ZAgMBAAEwDQYJKoZIhvcNAQEFBQADggEB
+AGVLrbXSANOMZpWevPJUMoZMJ4H26q9+tJ4kVi36ufQOaRBJ19lc73wt/5CL1Zzx
+uHBWJSMkUXn3CIXjyV9QsP3YF94yG9LghIKI/0Z+DK5j1TomplS13DZ+JuUIbogZ
+gwTXy/EuAxynUO+iyLD3c7/rJO94luWd2Ct9ljv9Kza7LxeEjKloBeGxddWgeU7/
+OdkPzLmvCxAsK/Wk4LAKG0p3ZwIqLdusMl6TBpStntLhh98M5xQXoozmRo8bBlp5
+kjItngdSWKWyXalw93SGEoPhe7u6fAxMBBuEpAtF5DS+mzTHB/wbJz1FuD3f973J
+4MeDFTIHtkTp/lYrbSmWvzU=
+-----END CERTIFICATE-----
diff --git a/tests/tls/server.key b/tests/tls/server.key
new file mode 100644
index 0000000..9a1f1dd
--- /dev/null
+++ b/tests/tls/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEArvViRBgQEQncqrfv4qed6fkuzdJShr9MO+S36lZXSEBmDAH/
+cUjHGz0k3bPa2KvIO12noTSetvMNIVpxwxFNcdvFCzJS9tqfdYpxJlgDcovn5QqK
+YFID3rEJHPpHF1Fb/vzn/HnJSFwsZ4uuyfHyu5x/+PDC/qnVnrPZ67TEQec/9pgu
+7HOd8eH2iXTqQsddwmh72DyByX0+3e4WkwwRi1QkgvdLQeVryuxNYSKCHXasK7YV
+qKUXEokpkHyWZij4D07Z1V9a95nixFXHS3GfTjvK9W7lKwXQeWduSua9JwmMwJOA
+MidaavLY2ozMLLp2yxTkOl/QzUQlThtFw6btGQIDAQABAoIBAFyq69lVRW1A4/go
+ZI6QaTu8F+Y8OCnWuPIgOqmMAb7rHSHPDRVbjtoGkLg8wvVwRyXqfRcNX+NW6OV5
+mjfPuk1MMhm0Fe1Z7ou7QCMnCuxo3fKamqBZ0GLrMgB/L5hSJ3/vRJCdkNcauwo9
+Gd8sn3xvb/jSzPVFzze32vzVSf39NSxcLRGnMb1Y/1U5jA7Bc+XbPb1fvWJ9ZJec
+o5wz0VLNWgYEt3dnvWZOf1ONx50ROwlKFOPhdYg/IQxzkWxOu6ZcleJrJsf5eCvj
+o2Ogm6bWMhTO73MYjHD0+hR6xl3g/vIBe+N4e4LmnI7BBQRe9SKrazqBt35/5Zs5
+IKFDPAECgYEA5M2sI7dnbHK1Ur+AIGEan3sWtbXOvg0xihCXRojE5oSKC5BFf5ny
+LvZ/VcszhjFv6ruUunXtN8qXJ8n3QQQXyuEeKpkGCvJqE8inREKSTexLj06niU1r
+w4XyyEvIckz42sv2k0mwH7qCIwAMkAQtqnM/ue2aCuyKKP5OLqqlSskCgYEAw8E/
+3zWbLwtWb6VtyaSv9/xHL71SXkxpY9FIc7MFhQLnvR6ZgZS42fXopeo04Byx6cZ3
+3QM8UPYE77H0ch3r1HdZIIXLw2aX8SnZtHtchU50cdmhCV51D6dk/bftxgncFzxp
+nHRwoPhJTXF/y0+jNP8m5Tn5YgxzJ9Sec7WuZ9ECgYEAnFqURMADNA/bKxXkR7wz
+xkIGDdyU0DkR3mhiB/hUnbZ641YOuBkKb99Qut8mcZB9C2puQ1Fs7tBJpQ4WId7b
+J2/Y/oEdqQNpS+W1sCbR9eAA7ohwYpp+htmVRBzNeJZzBImXEaWsbrI0VhilfRDt
+5+nj5Xmh588mxsapxKgmVkkCgYEAhJiaEy/UdgFQA0AjJbsQFwIjlgq/iHBp0tso
+IHba/kYBgvD/Oe7rZ3hSplAGkOfe+2McPfC7InwCy/nWgpYR8FEHZig65ZjQwuJ+
+POpyuTlzVsr7ccUxtfDFT7cOsF5tXq/lOb0FrYOA45xF3AmNm5BZYFvsuKWGOyyi
+R+6AvIECgYAXQ8Ud5GX0aXm8cRbwLStamooBreeCKQ9plLnXdqiwkjoeqhdHcWzh
+M4Cws86fbRSWESqvY3NVJCA5Na9HN0/LH/UlxN8tfrEfv1al/2UXN8zIL1a1uVfK
+H9CtL9znc7mJKBODBxDXgdC+QHMdtGwGU5QYTVwlPEBbdM/2JwgBfw==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/server.pem b/tests/tls/server.pem
new file mode 100644
index 0000000..83277c1
--- /dev/null
+++ b/tests/tls/server.pem
@@ -0,0 +1,47 @@
+-----BEGIN CERTIFICATE-----
+MIIDPTCCAiUCCQDiqmqW0b8a7jANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMF0xCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCu9WJEGBARCdyqt+/ip53p+S7N0lKGv0w75LfqVldIQGYMAf9xSMcb
+PSTds9rYq8g7XaehNJ628w0hWnHDEU1x28ULMlL22p91inEmWANyi+flCopgUgPe
+sQkc+kcXUVv+/Of8eclIXCxni67J8fK7nH/48ML+qdWes9nrtMRB5z/2mC7sc53x
+4faJdOpCx13CaHvYPIHJfT7d7haTDBGLVCSC90tB5WvK7E1hIoIddqwrthWopRcS
+iSmQfJZmKPgPTtnVX1r3meLEVcdLcZ9OO8r1buUrBdB5Z25K5r0nCYzAk4AyJ1pq
+8tjajMwsunbLFOQ6X9DNRCVOG0XDpu0ZAgMBAAEwDQYJKoZIhvcNAQEFBQADggEB
+AGVLrbXSANOMZpWevPJUMoZMJ4H26q9+tJ4kVi36ufQOaRBJ19lc73wt/5CL1Zzx
+uHBWJSMkUXn3CIXjyV9QsP3YF94yG9LghIKI/0Z+DK5j1TomplS13DZ+JuUIbogZ
+gwTXy/EuAxynUO+iyLD3c7/rJO94luWd2Ct9ljv9Kza7LxeEjKloBeGxddWgeU7/
+OdkPzLmvCxAsK/Wk4LAKG0p3ZwIqLdusMl6TBpStntLhh98M5xQXoozmRo8bBlp5
+kjItngdSWKWyXalw93SGEoPhe7u6fAxMBBuEpAtF5DS+mzTHB/wbJz1FuD3f973J
+4MeDFTIHtkTp/lYrbSmWvzU=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEArvViRBgQEQncqrfv4qed6fkuzdJShr9MO+S36lZXSEBmDAH/
+cUjHGz0k3bPa2KvIO12noTSetvMNIVpxwxFNcdvFCzJS9tqfdYpxJlgDcovn5QqK
+YFID3rEJHPpHF1Fb/vzn/HnJSFwsZ4uuyfHyu5x/+PDC/qnVnrPZ67TEQec/9pgu
+7HOd8eH2iXTqQsddwmh72DyByX0+3e4WkwwRi1QkgvdLQeVryuxNYSKCHXasK7YV
+qKUXEokpkHyWZij4D07Z1V9a95nixFXHS3GfTjvK9W7lKwXQeWduSua9JwmMwJOA
+MidaavLY2ozMLLp2yxTkOl/QzUQlThtFw6btGQIDAQABAoIBAFyq69lVRW1A4/go
+ZI6QaTu8F+Y8OCnWuPIgOqmMAb7rHSHPDRVbjtoGkLg8wvVwRyXqfRcNX+NW6OV5
+mjfPuk1MMhm0Fe1Z7ou7QCMnCuxo3fKamqBZ0GLrMgB/L5hSJ3/vRJCdkNcauwo9
+Gd8sn3xvb/jSzPVFzze32vzVSf39NSxcLRGnMb1Y/1U5jA7Bc+XbPb1fvWJ9ZJec
+o5wz0VLNWgYEt3dnvWZOf1ONx50ROwlKFOPhdYg/IQxzkWxOu6ZcleJrJsf5eCvj
+o2Ogm6bWMhTO73MYjHD0+hR6xl3g/vIBe+N4e4LmnI7BBQRe9SKrazqBt35/5Zs5
+IKFDPAECgYEA5M2sI7dnbHK1Ur+AIGEan3sWtbXOvg0xihCXRojE5oSKC5BFf5ny
+LvZ/VcszhjFv6ruUunXtN8qXJ8n3QQQXyuEeKpkGCvJqE8inREKSTexLj06niU1r
+w4XyyEvIckz42sv2k0mwH7qCIwAMkAQtqnM/ue2aCuyKKP5OLqqlSskCgYEAw8E/
+3zWbLwtWb6VtyaSv9/xHL71SXkxpY9FIc7MFhQLnvR6ZgZS42fXopeo04Byx6cZ3
+3QM8UPYE77H0ch3r1HdZIIXLw2aX8SnZtHtchU50cdmhCV51D6dk/bftxgncFzxp
+nHRwoPhJTXF/y0+jNP8m5Tn5YgxzJ9Sec7WuZ9ECgYEAnFqURMADNA/bKxXkR7wz
+xkIGDdyU0DkR3mhiB/hUnbZ641YOuBkKb99Qut8mcZB9C2puQ1Fs7tBJpQ4WId7b
+J2/Y/oEdqQNpS+W1sCbR9eAA7ohwYpp+htmVRBzNeJZzBImXEaWsbrI0VhilfRDt
+5+nj5Xmh588mxsapxKgmVkkCgYEAhJiaEy/UdgFQA0AjJbsQFwIjlgq/iHBp0tso
+IHba/kYBgvD/Oe7rZ3hSplAGkOfe+2McPfC7InwCy/nWgpYR8FEHZig65ZjQwuJ+
+POpyuTlzVsr7ccUxtfDFT7cOsF5tXq/lOb0FrYOA45xF3AmNm5BZYFvsuKWGOyyi
+R+6AvIECgYAXQ8Ud5GX0aXm8cRbwLStamooBreeCKQ9plLnXdqiwkjoeqhdHcWzh
+M4Cws86fbRSWESqvY3NVJCA5Na9HN0/LH/UlxN8tfrEfv1al/2UXN8zIL1a1uVfK
+H9CtL9znc7mJKBODBxDXgdC+QHMdtGwGU5QYTVwlPEBbdM/2JwgBfw==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tls/server_chain.pem b/tests/tls/server_chain.pem
new file mode 100644
index 0000000..d590275
--- /dev/null
+++ b/tests/tls/server_chain.pem
@@ -0,0 +1,67 @@
+-----BEGIN CERTIFICATE-----
+MIIDPTCCAiUCCQDiqmqW0b8a7jANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMF0xCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCu9WJEGBARCdyqt+/ip53p+S7N0lKGv0w75LfqVldIQGYMAf9xSMcb
+PSTds9rYq8g7XaehNJ628w0hWnHDEU1x28ULMlL22p91inEmWANyi+flCopgUgPe
+sQkc+kcXUVv+/Of8eclIXCxni67J8fK7nH/48ML+qdWes9nrtMRB5z/2mC7sc53x
+4faJdOpCx13CaHvYPIHJfT7d7haTDBGLVCSC90tB5WvK7E1hIoIddqwrthWopRcS
+iSmQfJZmKPgPTtnVX1r3meLEVcdLcZ9OO8r1buUrBdB5Z25K5r0nCYzAk4AyJ1pq
+8tjajMwsunbLFOQ6X9DNRCVOG0XDpu0ZAgMBAAEwDQYJKoZIhvcNAQEFBQADggEB
+AGVLrbXSANOMZpWevPJUMoZMJ4H26q9+tJ4kVi36ufQOaRBJ19lc73wt/5CL1Zzx
+uHBWJSMkUXn3CIXjyV9QsP3YF94yG9LghIKI/0Z+DK5j1TomplS13DZ+JuUIbogZ
+gwTXy/EuAxynUO+iyLD3c7/rJO94luWd2Ct9ljv9Kza7LxeEjKloBeGxddWgeU7/
+OdkPzLmvCxAsK/Wk4LAKG0p3ZwIqLdusMl6TBpStntLhh98M5xQXoozmRo8bBlp5
+kjItngdSWKWyXalw93SGEoPhe7u6fAxMBBuEpAtF5DS+mzTHB/wbJz1FuD3f973J
+4MeDFTIHtkTp/lYrbSmWvzU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDRDCCAiwCCQC5E5PSm8flUjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJa
+WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz
+dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5
+MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGQxCzAJBgNVBAYTAlpaMQowCAYDVQQI
+DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL
+DAEuMRkwFwYDVQQDDBBodHRwbGliMi10ZXN0LUNBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrj
+mlTHPFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gH
+sQn33Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOE
+Nh5rlYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0
+QByPPACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwC
+PspC+wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQB4b+DWt0An4YoXj7lb/+N7FVr2m5UVyBI+bbEGI/qsql/Ixiaef69M
+jej7n5ucUx8GBql62W0c3/E3qZFfo49ngH1WC5gkKQH9V4jGZui5CUfmNE6WepQ/
+vL6eKXUp7RoJ/hWVhGm1uV3OShF+EN0t2wZttYg4lip0FjrY8tRWdjw5yu61wWVu
+WuHxTzKiHe9emjhhUBgnWRnNeYPTRs0xM2Awv5KYPq2cmrjGbSz3mYDkBpbiJUp4
+pM9g8qLmsDO2yrlVF659D08+5zkmMbyqnn84X0n3SM3Yn0ayZOmbNHiXoAzklZNP
+7xiyxMEAfVQOITsvSDG2PzbZlGGtbaka
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEArvViRBgQEQncqrfv4qed6fkuzdJShr9MO+S36lZXSEBmDAH/
+cUjHGz0k3bPa2KvIO12noTSetvMNIVpxwxFNcdvFCzJS9tqfdYpxJlgDcovn5QqK
+YFID3rEJHPpHF1Fb/vzn/HnJSFwsZ4uuyfHyu5x/+PDC/qnVnrPZ67TEQec/9pgu
+7HOd8eH2iXTqQsddwmh72DyByX0+3e4WkwwRi1QkgvdLQeVryuxNYSKCHXasK7YV
+qKUXEokpkHyWZij4D07Z1V9a95nixFXHS3GfTjvK9W7lKwXQeWduSua9JwmMwJOA
+MidaavLY2ozMLLp2yxTkOl/QzUQlThtFw6btGQIDAQABAoIBAFyq69lVRW1A4/go
+ZI6QaTu8F+Y8OCnWuPIgOqmMAb7rHSHPDRVbjtoGkLg8wvVwRyXqfRcNX+NW6OV5
+mjfPuk1MMhm0Fe1Z7ou7QCMnCuxo3fKamqBZ0GLrMgB/L5hSJ3/vRJCdkNcauwo9
+Gd8sn3xvb/jSzPVFzze32vzVSf39NSxcLRGnMb1Y/1U5jA7Bc+XbPb1fvWJ9ZJec
+o5wz0VLNWgYEt3dnvWZOf1ONx50ROwlKFOPhdYg/IQxzkWxOu6ZcleJrJsf5eCvj
+o2Ogm6bWMhTO73MYjHD0+hR6xl3g/vIBe+N4e4LmnI7BBQRe9SKrazqBt35/5Zs5
+IKFDPAECgYEA5M2sI7dnbHK1Ur+AIGEan3sWtbXOvg0xihCXRojE5oSKC5BFf5ny
+LvZ/VcszhjFv6ruUunXtN8qXJ8n3QQQXyuEeKpkGCvJqE8inREKSTexLj06niU1r
+w4XyyEvIckz42sv2k0mwH7qCIwAMkAQtqnM/ue2aCuyKKP5OLqqlSskCgYEAw8E/
+3zWbLwtWb6VtyaSv9/xHL71SXkxpY9FIc7MFhQLnvR6ZgZS42fXopeo04Byx6cZ3
+3QM8UPYE77H0ch3r1HdZIIXLw2aX8SnZtHtchU50cdmhCV51D6dk/bftxgncFzxp
+nHRwoPhJTXF/y0+jNP8m5Tn5YgxzJ9Sec7WuZ9ECgYEAnFqURMADNA/bKxXkR7wz
+xkIGDdyU0DkR3mhiB/hUnbZ641YOuBkKb99Qut8mcZB9C2puQ1Fs7tBJpQ4WId7b
+J2/Y/oEdqQNpS+W1sCbR9eAA7ohwYpp+htmVRBzNeJZzBImXEaWsbrI0VhilfRDt
+5+nj5Xmh588mxsapxKgmVkkCgYEAhJiaEy/UdgFQA0AjJbsQFwIjlgq/iHBp0tso
+IHba/kYBgvD/Oe7rZ3hSplAGkOfe+2McPfC7InwCy/nWgpYR8FEHZig65ZjQwuJ+
+POpyuTlzVsr7ccUxtfDFT7cOsF5tXq/lOb0FrYOA45xF3AmNm5BZYFvsuKWGOyyi
+R+6AvIECgYAXQ8Ud5GX0aXm8cRbwLStamooBreeCKQ9plLnXdqiwkjoeqhdHcWzh
+M4Cws86fbRSWESqvY3NVJCA5Na9HN0/LH/UlxN8tfrEfv1al/2UXN8zIL1a1uVfK
+H9CtL9znc7mJKBODBxDXgdC+QHMdtGwGU5QYTVwlPEBbdM/2JwgBfw==
+-----END RSA PRIVATE KEY-----