Skip to content
Snippets Groups Projects
test_matrix_federation_agent.py 43.9 KiB
Newer Older
  • Learn to ignore specific revisions
  •         it ignores transient errors.
            """
    
            self.reactor.lookups["testserv"] = "1.2.3.4"
    
    
            fetch_d = self.well_known_resolver.get_well_known(b"testserv")
    
    
            # there should be an attempt to connect on port 443 for the .well-known
            clients = self.reactor.tcpClients
            self.assertEqual(len(clients), 1)
            (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
            self.assertEqual(host, "1.2.3.4")
            self.assertEqual(port, 443)
    
            well_known_server = self._handle_well_known_connection(
                client_factory,
                expected_sni=b"testserv",
                response_headers={b"Cache-Control": b"max-age=1000"},
                content=b'{ "m.server": "target-server" }',
            )
    
            r = self.successResultOf(fetch_d)
            self.assertEqual(r.delegated_server, b"target-server")
    
            # close the tcp connection
            well_known_server.loseConnection()
    
            # Get close to the cache expiry, this will cause the resolver to do
            # another lookup.
            self.reactor.pump((900.0,))
    
    
            fetch_d = self.well_known_resolver.get_well_known(b"testserv")
    
            # The resolver may retry a few times, so fonx all requests that come along
            attempts = 0
            while self.reactor.tcpClients:
                clients = self.reactor.tcpClients
                (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
    
                attempts += 1
    
                # fonx the connection attempt, this will be treated as a temporary
                # failure.
                client_factory.clientConnectionFailed(None, Exception("nope"))
    
                # There's a few sleeps involved, so we have to pump the reactor a
                # bit.
                self.reactor.pump((1.0, 1.0))
    
            # We expect to see more than one attempt as there was previously a valid
            # well known.
            self.assertGreater(attempts, 1)
    
    
            # Resolver should return cached value, despite the lookup failing.
            r = self.successResultOf(fetch_d)
            self.assertEqual(r.delegated_server, b"target-server")
    
    
            # Expire both caches and repeat the request
            self.reactor.pump((10000.0,))
    
    
            # Repated the request, this time it should fail if the lookup fails.
    
            fetch_d = self.well_known_resolver.get_well_known(b"testserv")
    
    
            clients = self.reactor.tcpClients
            (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
            client_factory.clientConnectionFailed(None, Exception("nope"))
            self.reactor.pump((0.4,))
    
            r = self.successResultOf(fetch_d)
            self.assertEqual(r.delegated_server, None)
    
    
        def test_srv_fallbacks(self):
            """Test that other SRV results are tried if the first one fails.
            """
    
            self.mock_resolver.resolve_service.side_effect = lambda _: [
                Server(host=b"target.com", port=8443),
                Server(host=b"target.com", port=8444),
            ]
            self.reactor.lookups["target.com"] = "1.2.3.4"
    
            test_d = self._make_get_request(b"matrix://testserv/foo/bar")
    
            # Nothing happened yet
            self.assertNoResult(test_d)
    
            self.mock_resolver.resolve_service.assert_called_once_with(
                b"_matrix._tcp.testserv"
            )
    
            # We should see an attempt to connect to the first server
            clients = self.reactor.tcpClients
            self.assertEqual(len(clients), 1)
            (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
            self.assertEqual(host, "1.2.3.4")
            self.assertEqual(port, 8443)
    
            # Fonx the connection
            client_factory.clientConnectionFailed(None, Exception("nope"))
    
            # There's a 300ms delay in HostnameEndpoint
            self.reactor.pump((0.4,))
    
            # Hasn't failed yet
            self.assertNoResult(test_d)
    
            # We shouldnow see an attempt to connect to the second server
            clients = self.reactor.tcpClients
            self.assertEqual(len(clients), 1)
            (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
            self.assertEqual(host, "1.2.3.4")
            self.assertEqual(port, 8444)
    
            # make a test server, and wire up the client
            http_server = self._make_connection(client_factory, expected_sni=b"testserv")
    
            self.assertEqual(len(http_server.requests), 1)
            request = http_server.requests[0]
            self.assertEqual(request.method, b"GET")
            self.assertEqual(request.path, b"/foo/bar")
            self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"testserv"])
    
            # finish the request
            request.finish()
            self.reactor.pump((0.1,))
            self.successResultOf(test_d)
    
    
    class TestCachePeriodFromHeaders(unittest.TestCase):
    
        def test_cache_control(self):
            # uppercase
            self.assertEqual(
                _cache_period_from_headers(
    
    Amber Brown's avatar
    Amber Brown committed
                    Headers({b"Cache-Control": [b"foo, Max-Age = 100, bar"]})
    
            self.assertIsNone(
    
    Amber Brown's avatar
    Amber Brown committed
                _cache_period_from_headers(Headers({b"Cache-Control": [b"max-age=, bar"]}))
    
    
            # hackernews: bogus due to semicolon
    
            self.assertIsNone(
                _cache_period_from_headers(
    
    Amber Brown's avatar
    Amber Brown committed
                    Headers({b"Cache-Control": [b"private; max-age=0"]})
    
    
            # github
            self.assertEqual(
                _cache_period_from_headers(
    
    Amber Brown's avatar
    Amber Brown committed
                    Headers({b"Cache-Control": [b"max-age=0, private, must-revalidate"]})
    
            )
    
            # google
            self.assertEqual(
                _cache_period_from_headers(
    
    Amber Brown's avatar
    Amber Brown committed
                    Headers({b"cache-control": [b"private, max-age=0"]})
    
            )
    
        def test_expires(self):
            self.assertEqual(
                _cache_period_from_headers(
    
    Amber Brown's avatar
    Amber Brown committed
                    Headers({b"Expires": [b"Wed, 30 Jan 2019 07:35:33 GMT"]}),
    
                    time_now=lambda: 1548833700,
                ),
                33,
    
            )
    
            # cache-control overrides expires
            self.assertEqual(
                _cache_period_from_headers(
    
    Amber Brown's avatar
    Amber Brown committed
                            b"cache-control": [b"max-age=10"],
                            b"Expires": [b"Wed, 30 Jan 2019 07:35:33 GMT"],
    
                        }
                    ),
                    time_now=lambda: 1548833700,
                ),
                10,
    
            )
    
            # invalid expires means immediate expiry
    
    Amber Brown's avatar
    Amber Brown committed
            self.assertEqual(_cache_period_from_headers(Headers({b"Expires": [b"0"]})), 0)
    
    
    def _check_logcontext(context):
        current = LoggingContext.current_context()
        if current is not context:
    
            raise AssertionError("Expected logcontext %s but was %s" % (context, current))
    
    def _build_test_server(connection_creator):
    
        """Construct a test server
    
        This builds an HTTP channel, wrapped with a TLSMemoryBIOProtocol
    
    
        Args:
            connection_creator (IOpenSSLServerConnectionCreator): thing to build
                SSL connections
            sanlist (list[bytes]): list of the SAN entries for the cert returned
                by the server
    
    
        Returns:
            TLSMemoryBIOProtocol
        """
        server_factory = Factory.forProtocol(HTTPChannel)
        # Request.finish expects the factory to have a 'log' method.
        server_factory.log = _log_request
    
        server_tls_factory = TLSMemoryBIOFactory(
    
            connection_creator, isClient=False, wrappedFactory=server_factory
    
        )
    
        return server_tls_factory.buildProtocol(None)
    
    
    def _log_request(request):
        """Implements Factory.log, which is expected by Request.finish"""
        logger.info("Completed request %s", request)
    
    
    
    @implementer(IPolicyForHTTPS)
    class TrustingTLSPolicyForHTTPS(object):
    
        """An IPolicyForHTTPS which checks that the certificate belongs to the
        right server, but doesn't check the certificate chain."""
    
        def creatorForNetloc(self, hostname, port):
            certificateOptions = OpenSSLCertificateOptions()
            return ClientTLSOptions(hostname, certificateOptions.getContext())