Hello community, here is the log from the commit of package python-TxSNI for openSUSE:Factory checked in at 2020-04-18 00:32:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-TxSNI (Old) and /work/SRC/openSUSE:Factory/.python-TxSNI.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-TxSNI" Sat Apr 18 00:32:00 2020 rev:3 rq:794853 version:0.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-TxSNI/python-TxSNI.changes 2019-06-13 23:01:32.823509924 +0200 +++ /work/SRC/openSUSE:Factory/.python-TxSNI.new.2738/python-TxSNI.changes 2020-04-18 00:33:21.490416063 +0200 @@ -1,0 +2,9 @@ +Fri Apr 17 09:38:39 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com> + +- Update to 0.20.0 + * Support new OpenSSL + * Test ALPN and NPN separately. + * Switch cert_builder from print to twisted.logger. +- Drop unneeded openssl111.patch + +------------------------------------------------------------------- Old: ---- openssl111.patch python-TxSNI-0.1.9.tar.gz New: ---- python-TxSNI-0.2.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-TxSNI.spec ++++++ --- /var/tmp/diff_new_pack.bxa2Kl/_old 2020-04-18 00:33:22.178417488 +0200 +++ /var/tmp/diff_new_pack.bxa2Kl/_new 2020-04-18 00:33:22.182417497 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-TxSNI # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,14 +18,13 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-TxSNI -Version: 0.1.9 +Version: 0.2.0 Release: 0 Summary: Python module for running a TLS server with Twisted License: MIT Group: Development/Languages/Python URL: https://github.com/glyph/txsni Source0: https://github.com/glyph/txsni/archive/v%{version}/%{name}-%{version}.tar.gz -Patch0: openssl111.patch BuildRequires: %{python_module Twisted} >= 14.0.0 BuildRequires: %{python_module pyOpenSSL} >= 0.14 BuildRequires: fdupes @@ -40,7 +39,6 @@ %prep %setup -q -n txsni-%{version} -%patch0 -p1 %build %python_build ++++++ python-TxSNI-0.1.9.tar.gz -> python-TxSNI-0.2.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/.travis/install.sh new/txsni-0.2.0/.travis/install.sh --- old/txsni-0.1.9/.travis/install.sh 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/.travis/install.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,34 +0,0 @@ -#!/bin/bash - -set -e -set -x - -# Upgrade packaging tools separately, so that other installations are -# performed with the upgraded tools. -pip install -U pip setuptools wheel -pip install tox codecov - -if [ "${TOXENV::5}" == "pypy-" ]; then - git clone --depth 1 https://github.com/yyuu/pyenv.git ~/.pyenv - PYENV_ROOT="$HOME/.pyenv" - PATH="$PYENV_ROOT/bin:$PATH" - eval "$(pyenv init -)" - pyenv install pypy-4.0.1 - pyenv global pypy-4.0.1 -fi - -if [[ -v OPENSSL_VERSION ]]; then - OPENSSL_DIR="${HOME}/ossl" - mkdir -p "${OPENSSL_DIR}" - curl -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz - tar zxf openssl-$OPENSSL_VERSION.tar.gz - cd openssl-$OPENSSL_VERSION - ./config shared no-asm no-ssl2 -fPIC --prefix="${OPENSSL_DIR}" - # modify the shlib version to a unique one to make sure the dynamic - # linker doesn't load the system one. - sed -i "s/^SHLIB_MAJOR=.*/SHLIB_MAJOR=100/" Makefile - sed -i "s/^SHLIB_MINOR=.*/SHLIB_MINOR=0.0/" Makefile - sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=100.0.0/" Makefile - make depend - make install -fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/.travis/run.sh new/txsni-0.2.0/.travis/run.sh --- old/txsni-0.1.9/.travis/run.sh 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/.travis/run.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,21 +0,0 @@ -#!/bin/bash - -set -e -set -x - -if [ "${TOXENV::5}" == "pypy-" ]; then - PYENV_ROOT="$HOME/.pyenv" - PATH="$PYENV_ROOT/bin:$PATH" - eval "$(pyenv init -)" -fi - -if [[ -v OPENSSL_VERSION ]]; then - OPENSSL_DIR="${HOME}/ossl" - - export PATH="$HOME/$OPENSSL_DIR/bin:$PATH" - export CFLAGS="-I$HOME/$OPENSSL_DIR/include" - # rpath on linux will cause it to use an absolute path so we don't need to do LD_LIBRARY_PATH - export LDFLAGS="-L$HOME/$OPENSSL_DIR/lib -Wl,-rpath=$HOME/$OPENSSL_DIR/lib" -fi - -tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/.travis.yml new/txsni-0.2.0/.travis.yml --- old/txsni-0.1.9/.travis.yml 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/.travis.yml 2020-04-06 08:15:02.000000000 +0200 @@ -4,29 +4,23 @@ branches: only: - master -python: - - 2.7 -env: - - TOXENV=py27-twlatest OPENSSL_VERSION=1.0.2h - - TOXENV=py27-twlatest - - TOXENV=py36-twlatest - - TOXENV=pypy-twlatest OPENSSL_VERSION=1.0.2h - - TOXENV=pypy-twlatest - - TOXENV=py27-tw14 OPENSSL_VERSION=1.0.2h - - TOXENV=py27-tw14 - - TOXENV=pypy-tw14 OPENSSL_VERSION=1.0.2h - - TOXENV=pypy-tw14 - - TOXENV=py27-twtrunk OPENSSL_VERSION=1.0.2h - - TOXENV=py27-twtrunk - - TOXENV=pypy-twtrunk OPENSSL_VERSION=1.0.2h - - TOXENV=pypy-twtrunk - -install: - - "./.travis/install.sh" +matrix: + include: + - env: TOXENV=py27-twlatest + python: 2.7 + - env: TOXENV=py36-twlatest + python: 3.6 + - env: TOXENV=py36-twtrunk + python: 3.6 + - env: TOXENV=pypy3-twlatest + python: "pypy3" + - env: TOXENV=pypy3-twtrunk + python: "pypy3" script: - - "./.travis/run.sh" + - pip install tox codecov + - tox after_success: # Codecov needs combined coverage, and having the raw report in the test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/README.rst new/txsni-0.2.0/README.rst --- old/txsni-0.1.9/README.rst 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/README.rst 2020-04-06 08:15:02.000000000 +0200 @@ -15,7 +15,7 @@ $ cat public-stuff/mydomain.crt.pem >> certificates/mydomain.example.com.pem $ cat public-stuff/my-certificate-authority-chain.crt.pem >> \ certificates/mydomain.example.com.pem - $ twistd -n web --port txsni:certificates:tcp:443 + $ twist web --port txsni:certificates:tcp:443 Enjoy! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/setup.py new/txsni-0.2.0/setup.py --- old/txsni-0.1.9/setup.py 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/setup.py 2020-04-06 08:15:02.000000000 +0200 @@ -21,7 +21,7 @@ "Twisted[tls]>=14.0", "pyOpenSSL>=0.14", ], - version="0.1.9", + version="0.2.0", long_description=long_description, license="MIT", url="https://github.com/glyph/txsni", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/tox.ini new/txsni-0.2.0/tox.ini --- old/txsni-0.1.9/tox.ini 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/tox.ini 2020-04-06 08:15:02.000000000 +0200 @@ -1,12 +1,14 @@ [tox] -envlist = coverage-clean,{py27,pypy,py34,py35,py36}-{twtrunk,twlatest,tw14},coverage-report +envlist = coverage-clean,py27-twlatest,{pypy3,py36}-{twtrunk,twlatest},coverage-report [testenv:coverage-clean] +depends = deps = coverage skip_install = true commands = coverage erase [testenv:coverage-report] +depends = {py27,pypy3,py36}-{twtrunk,twlatest} deps = coverage skip_install = true commands = @@ -14,12 +16,12 @@ coverage report [testenv] +depends = coverage-clean whitelist_externals = mkdir deps = twlatest: Twisted[tls] twtrunk: https://github.com/twisted/twisted/archive/trunk.zip#egg=Twisted[tls] - tw132: Twisted[tls]==14.0 coverage cryptography commands = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/txsni/maputils.py new/txsni-0.2.0/txsni/maputils.py --- old/txsni-0.1.9/txsni/maputils.py 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/txsni/maputils.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,18 +0,0 @@ - - -class Cache(object): - - def __init__(self, mapping): - self.mapping = mapping - self.cache = {} - - - def __getitem__(self, key): - if key in self.cache: - return self.cache[key] - else: - value = self.mapping[key] - self.cache[key] = value - return value - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/txsni/only_noticed_pypi_pem_after_i_wrote_this.py new/txsni-0.2.0/txsni/only_noticed_pypi_pem_after_i_wrote_this.py --- old/txsni-0.1.9/txsni/only_noticed_pypi_pem_after_i_wrote_this.py 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/txsni/only_noticed_pypi_pem_after_i_wrote_this.py 2020-04-06 08:15:02.000000000 +0200 @@ -12,6 +12,7 @@ """ certificates = [] keys = [] + blobs = [b""] for line in pemdata.split(b"\n"): if line.startswith(b'-----BEGIN'): if b'CERTIFICATE' in line: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/txsni/parser.py new/txsni-0.2.0/txsni/parser.py --- old/txsni-0.1.9/txsni/parser.py 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/txsni/parser.py 2020-04-06 08:15:02.000000000 +0200 @@ -8,7 +8,6 @@ from twisted.plugin import IPlugin from txsni.snimap import SNIMap -from txsni.maputils import Cache from txsni.snimap import HostDirectoryMap from twisted.python.filepath import FilePath from txsni.tlsendpoint import TLSEndpoint @@ -23,9 +22,7 @@ return ':'.join([item.replace(':', '\\:') for item in items]) sub = colonJoin(list(args) + ['='.join(item) for item in kw.items()]) subEndpoint = serverFromString(reactor, sub) - contextFactory = SNIMap( - Cache(HostDirectoryMap(FilePath(expanduser(pemdir)))) - ) + contextFactory = SNIMap(HostDirectoryMap(FilePath(expanduser(pemdir)))) return TLSEndpoint(endpoint=subEndpoint, contextFactory=contextFactory) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/txsni/snimap.py new/txsni-0.2.0/txsni/snimap.py --- old/txsni-0.1.9/txsni/snimap.py 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/txsni/snimap.py 2020-04-06 08:15:02.000000000 +0200 @@ -5,7 +5,7 @@ from OpenSSL.SSL import Connection from twisted.internet.interfaces import IOpenSSLServerConnectionCreator -from twisted.internet.ssl import CertificateOptions, ContextFactory +from twisted.internet.ssl import CertificateOptions from txsni.only_noticed_pypi_pem_after_i_wrote_this import ( certificateOptionsFromPileOfPEM @@ -67,8 +67,8 @@ def __setattr__(self, attr, val): if attr in ('_obj', '_factory'): self.__dict__[attr] = val - - return setattr(self._obj, attr, val) + else: + setattr(self._obj, attr, val) def __delattr__(self, attr): return delattr(self._obj, attr) @@ -106,8 +106,8 @@ def __setattr__(self, attr, val): if attr in ('_obj', '_factory'): self.__dict__[attr] = val - - return setattr(self._obj, attr, val) + else: + return setattr(self._obj, attr, val) def __delattr__(self, attr): return delattr(self._obj, attr) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/txsni/test/certs/cert_builder.py new/txsni-0.2.0/txsni/test/certs/cert_builder.py --- old/txsni-0.1.9/txsni/test/certs/cert_builder.py 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/txsni/test/certs/cert_builder.py 2020-04-06 08:15:02.000000000 +0200 @@ -1,10 +1,13 @@ -from __future__ import print_function, absolute_import +from __future__ import absolute_import from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID + +from twisted.logger import Logger + import datetime import uuid import os @@ -35,6 +38,8 @@ ] +_LOGGER = Logger() + def _build_root_cert(): """ Builds a single root certificate that can be used to sign the others. This @@ -43,7 +48,7 @@ build the leaves. """ if os.path.isfile(ROOT_CERT_PATH) and os.path.isfile(ROOT_KEY_PATH): - print("Root already exists, not regenerating.") + _LOGGER.info("Root already exists, not regenerating.") with open(ROOT_CERT_PATH, 'rb') as f: certificate = x509.load_pem_x509_certificate( f.read(), default_backend() @@ -99,7 +104,7 @@ certificate.public_bytes(serialization.Encoding.PEM) ) - print("Built root certificate.") + _LOGGER.info("Built root certificate.") return certificate, private_key @@ -109,7 +114,8 @@ Builds a single leaf certificate, signed by the CA's private key. """ if os.path.isfile(certfile): - print("%s already exists, not regenerating" % hostname) + _LOGGER.info("{hostname} already exists, not regenerating", + hostname=hostname) return private_key = rsa.generate_private_key( @@ -158,7 +164,7 @@ certificate.public_bytes(serialization.Encoding.PEM) ) - print("Built certificate for %s" % hostname) + _LOGGER.info("Built certificate for {hostname}", hostname=hostname) def _build_certs(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/txsni-0.1.9/txsni/test/test_txsni.py new/txsni-0.2.0/txsni/test/test_txsni.py --- old/txsni-0.1.9/txsni/test/test_txsni.py 2017-05-25 04:03:50.000000000 +0200 +++ new/txsni-0.2.0/txsni/test/test_txsni.py 2020-04-06 08:15:02.000000000 +0200 @@ -1,11 +1,14 @@ from __future__ import absolute_import -import os +from functools import partial from txsni.snimap import SNIMap, HostDirectoryMap from txsni.tlsendpoint import TLSEndpoint +from txsni.only_noticed_pypi_pem_after_i_wrote_this import objectsFromPEM +from txsni.parser import SNIDirectoryParser from OpenSSL.crypto import load_certificate, FILETYPE_PEM +from OpenSSL.SSL import Context, SSLv23_METHOD, Connection from twisted.internet import protocol, endpoints, reactor, defer, interfaces from twisted.internet.ssl import ( @@ -17,7 +20,7 @@ from zope.interface import implementer from .certs.cert_builder import ( - ROOT_CERT_PATH, HTTP2BIN_CERT_PATH, _build_certs, CERT_DIR + ROOT_CERT_PATH, HTTP2BIN_CERT_PATH, CERT_DIR, _build_certs, ) # We need some temporary certs. @@ -47,7 +50,13 @@ return wrapper_endpoint -def handshake(client_factory, server_factory, hostname, server_endpoint): +def handshake( + client_factory, + server_factory, + hostname, + server_endpoint, + acceptable_protocols=None, +): """ Connect a basic Twisted TLS client endpoint to the provided TxSNI TLSEndpoint. Returns a Deferred that fires when the connection has been @@ -56,12 +65,18 @@ """ def connect_client(listening_port): port_number = listening_port.getHost().port - client = endpoints.TCP4ClientEndpoint( reactor, '127.0.0.1', port_number ) + + maybe_alpn = {} + if acceptable_protocols is not None: + maybe_alpn['acceptableProtocols'] = acceptable_protocols + options = optionsForClientTLS( - hostname=hostname, trustRoot=PEM_ROOT + hostname=hostname, + trustRoot=PEM_ROOT, + **maybe_alpn ) client = endpoints.wrapClientTLS(options, client) connectDeferred = client.connect(client_factory) @@ -88,11 +103,8 @@ def dataReceived(self, data): cert = self.transport.getPeerCertificate() + proto = self.transport.negotiatedProtocol - if not skipNegotiation: - proto = self.transport.negotiatedProtocol - else: - proto = None self.transport.abortConnection() self.handshake_deferred.callback((cert, proto)) self.handshake_deferred = None @@ -120,23 +132,18 @@ self.transport.loseConnection() -try: - @implementer(interfaces.IProtocolNegotiationFactory) - class NegotiatingFactory(protocol.Factory): - """ - A Twisted Protocol Factory that implements the protocol negotiation - extensions - """ - def acceptableProtocols(self): - return [b'h2', b'http/1.1'] - - class WritingNegotiatingFactory(WritingProtocolFactory, - NegotiatingFactory): - pass - - skipNegotiation = False -except AttributeError: - skipNegotiation = "IProtocolNegotiationFactory not supported" +@implementer(interfaces.IProtocolNegotiationFactory) +class NegotiatingFactory(protocol.Factory): + """ + A Twisted Protocol Factory that implements the protocol negotiation + extensions + """ + def acceptableProtocols(self): + return [b'h2', b'http/1.1'] + +class WritingNegotiatingFactory(WritingProtocolFactory, + NegotiatingFactory): + pass class TestSNIMap(unittest.TestCase): @@ -168,24 +175,26 @@ self.assertIsNot(conn.get_context(), options.getContext()) self.assertIsNotNone(conn.get_context()) +def assert_cert_is(test_case, protocol_cert, cert_path): + """ + Assert that ``protocol_cert`` is the same certificate as the one at + ``cert_path``. + """ + with open(cert_path, 'rb') as f: + target_cert = load_certificate(FILETYPE_PEM, f.read()) + + test_case.assertEqual( + protocol_cert.digest('sha256'), + target_cert.digest('sha256') + ) + + class TestCommunication(unittest.TestCase): """ Tests that use the full Twisted logic to validate that txsni works as expected. """ - def assertCertIs(self, protocol_cert, cert_path): - """ - Assert that ``protocol_cert`` is the same certificate as the one at - ``cert_path``. - """ - with open(cert_path, 'rb') as f: - target_cert = load_certificate(FILETYPE_PEM, f.read()) - - self.assertEqual( - protocol_cert.digest('sha256'), - target_cert.digest('sha256') - ) def test_specific_certificate(self): """ @@ -206,7 +215,7 @@ def confirm_cert(args): cert, proto = args - self.assertCertIs(cert, HTTP2BIN_CERT_PATH) + assert_cert_is(self, cert, HTTP2BIN_CERT_PATH) return d def close(args): @@ -218,16 +227,42 @@ return handshake_deferred +class TestPemObjects(unittest.TestCase, object): + """ + Tests for L{objectsFromPEM} + """ + + def test_noObjects(self): + """ + The empty string returns an empty list of certificates. + """ + + objects = objectsFromPEM(b"") + self.assertEqual(objects.certificates, []) + self.assertEqual(objects.keys, []) + + + +def will_use_tls_1_3(): + """ + Will OpenSSL negotiate TLS 1.3? + """ + ctx = Context(SSLv23_METHOD) + connection = Connection(ctx, None) + return connection.get_protocol_version_name() == u'TLSv1.3' + + class TestNegotiationStillWorks(unittest.TestCase): """ Tests that TxSNI doesn't break protocol negotiation. """ - if skipNegotiation: - skip = skipNegotiation - def test_specific_cert_still_negotiates(self): + EXPECTED_PROTOCOL = b'h2' + + def assert_specific_cert_still_negotiates(self, perform_handshake): """ - When TxSNI selects a specific cert, protocol negotiation still works. + When TxSNI selects a specific cert, protocol negotiation still + works. """ handshake_deferred = defer.Deferred() client_factory = WritingNegotiatingFactory(handshake_deferred) @@ -236,7 +271,7 @@ ) endpoint = sni_endpoint() - d = handshake( + d = perform_handshake( client_factory=client_factory, server_factory=server_factory, hostname=u'http2bin.org', @@ -245,7 +280,7 @@ def confirm_cert(args): cert, proto = args - self.assertEqual(proto, b'h2') + self.assertEqual(proto, self.EXPECTED_PROTOCOL) return d def close(args): @@ -255,3 +290,89 @@ handshake_deferred.addCallback(confirm_cert) handshake_deferred.addCallback(close) return handshake_deferred + + + def test_specific_cert_still_negotiates_with_alpn(self): + """ + When TxSNI selects a specific cert, Application Level Protocol + Negotiation (ALPN) still works. + """ + return self.assert_specific_cert_still_negotiates( + partial(handshake, acceptable_protocols=[self.EXPECTED_PROTOCOL]) + ) + + + def test_specific_cert_still_negotiates_with_npn(self): + """ + When TxSNI selects a specific cert, Next Protocol Negotiation + (NPN) still works. + """ + return self.assert_specific_cert_still_negotiates(handshake) + + if will_use_tls_1_3(): + test_specific_cert_still_negotiates_with_npn.skip = ( + "OpenSSL does not support NPN with TLS 1.3" + ) + + +class TestSNIDirectoryParser(unittest.TestCase): + """ + Tests the C{txsni} endpoint implementation. + """ + + def setUp(self): + self.directory_parser = SNIDirectoryParser() + + def test_recreated_certificates(self): + """ + L{SNIDirectoryParser} always uses the latest certificate for + the requested domain. + """ + endpoint = self.directory_parser.parseStreamServer( + reactor, CERT_DIR, 'tcp', port='0', interface='127.0.0.1') + + def handshake_and_check(_): + handshake_deferred = defer.Deferred() + client_factory = WritingProtocolFactory(handshake_deferred) + server_factory = protocol.Factory.forProtocol(WriteBackProtocol) + + initiate_handshake_deferred = handshake( + client_factory=client_factory, + server_factory=server_factory, + hostname=u"http2bin.org", + server_endpoint=endpoint, + ) + + def confirm_cert(args): + cert, proto = args + assert_cert_is(self, cert, HTTP2BIN_CERT_PATH) + + def close(args): + client, port = args + port.stopListening() + + exception = [None] + + def captureException(f): + exception[0] = f + + def maybeRethrow(_): + if exception[0] is not None: + exception[0].raiseException() + + handshake_deferred.addCallback(confirm_cert) + handshake_deferred.addErrback(captureException) + + handshake_deferred.addCallback(lambda _: initiate_handshake_deferred) + handshake_deferred.addCallback(close) + + handshake_deferred.addCallback(maybeRethrow) + return handshake_deferred + + def reset_http2bin_cert(_): + FilePath(HTTP2BIN_CERT_PATH).remove() + _build_certs() + + old_cert_handshake = handshake_and_check(None) + old_cert_handshake.addCallback(reset_http2bin_cert) + return old_cert_handshake.addCallback(handshake_and_check)