def connect_any_redis_attempts(self): """If redis is enabled we need to deal with workers connecting to a redis server. We don't want to use a real Redis server so we use a fake one. """ clients = self.reactor.tcpClients self.assertEqual(len(clients), 1) (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0) self.assertEqual(host, "localhost") self.assertEqual(port, 6379) client_protocol = client_factory.buildProtocol(None) server_protocol = self._redis_server.buildProtocol(None) client_to_server_transport = FakeTransport(server_protocol, self.reactor, client_protocol) client_protocol.makeConnection(client_to_server_transport) server_to_client_transport = FakeTransport(client_protocol, self.reactor, server_protocol) server_protocol.makeConnection(server_to_client_transport) return client_to_server_transport, server_to_client_transport
def test_overlong_title(self): self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")] end_content = (b"<html><head>" b"<title>" + b"x" * 2000 + b"</title>" b'<meta property="og:description" content="hi" />' b"</head></html>") channel = self.make_request( "GET", "preview_url?url=http://matrix.org", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( (b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: text/html; charset="windows-1251"\r\n\r\n') % (len(end_content), ) + end_content) self.pump() self.assertEqual(channel.code, 200) res = channel.json_body # We should only see the `og:description` field, as `title` is too long and should be stripped out self.assertCountEqual(["og:description"], res.keys())
def test_nonexistent_image(self) -> None: """If the preview image doesn't exist, ensure some data is returned.""" self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")] end_content = ( b"""<html><body><img src="http://cdn.matrix.org/foo.jpg"></body></html>""" ) channel = self.make_request( "GET", "preview_url?url=http://matrix.org", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( (b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: text/html; charset="utf8"\r\n\r\n') % (len(end_content), ) + end_content) self.pump() self.assertEqual(channel.code, 200) # The image should not be in the result. self.assertNotIn("og:image", channel.json_body)
def test_inline_data_url(self) -> None: """ An inline image (as a data URL) should be parsed properly. """ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")] data = base64.b64encode(SMALL_PNG) end_content = (b"<html><head>" b'<img src="data:image/png;base64,%s" />' b"</head></html>") % (data, ) channel = self.make_request( "GET", "preview_url?url=http://matrix.org", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( (b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: text/html; charset="utf8"\r\n\r\n') % (len(end_content), ) + end_content) self.pump() self.assertEqual(channel.code, 200) self._assert_small_png(channel.json_body)
def _handle_http_replication_attempt(self, hs, repl_port): """Handles a connection attempt to the given HS replication HTTP listener on the given port. """ # We should have at least one outbound connection attempt, where the # last is one to the HTTP repication IP/port. clients = self.reactor.tcpClients self.assertGreaterEqual(len(clients), 1) (host, port, client_factory, _timeout, _bindAddress) = clients.pop() self.assertEqual(host, "1.2.3.4") self.assertEqual(port, repl_port) # Set up client side protocol client_protocol = client_factory.buildProtocol(None) request_factory = OneShotRequestFactory() # Set up the server side protocol channel = _PushHTTPChannel(self.reactor) channel.requestFactory = request_factory channel.site = self._hs_to_site[hs] # Connect client to server and vice versa. client_to_server_transport = FakeTransport(channel, self.reactor, client_protocol) client_protocol.makeConnection(client_to_server_transport) server_to_client_transport = FakeTransport(client_protocol, self.reactor, channel) channel.makeConnection(server_to_client_transport)
def test_audio_rejected(self) -> None: self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")] end_content = b"anything" channel = self.make_request( "GET", "preview_url?url=http://matrix.org", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived((b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b"Content-Type: audio/aac\r\n\r\n") % (len(end_content)) + end_content) self.pump() self.assertEqual(channel.code, 502) self.assertEqual( channel.json_body, { "errcode": "M_UNKNOWN", "error": "Requested file's content type not allowed for this operation: audio/aac", }, )
def prepare(self, reactor, clock, hs): self.master_store = self.hs.get_datastore() self.storage = hs.get_storage() self.slaved_store = self.STORE_TYPE(Database(hs), self.hs.get_db_conn(), self.hs) self.event_id = 0 server_factory = ReplicationStreamProtocolFactory(self.hs) self.streamer = server_factory.streamer handler_factory = Mock() self.replication_handler = ReplicationClientHandler(self.slaved_store) self.replication_handler.factory = handler_factory client_factory = ReplicationClientFactory(self.hs, "client_name", self.replication_handler) server = server_factory.buildProtocol(None) client = client_factory.buildProtocol(None) client.makeConnection(FakeTransport(server, reactor)) self.server_to_client_transport = FakeTransport(client, reactor) server.makeConnection(self.server_to_client_transport)
def test_non_ascii_preview_httpequiv(self): self.lookups["matrix.org"] = [(IPv4Address, "8.8.8.8")] end_content = ( b'<html><head>' b'<meta http-equiv="Content-Type" content="text/html; charset=windows-1251"/>' b'<meta property="og:title" content="\xe4\xea\xe0" />' b'<meta property="og:description" content="hi" />' b'</head></html>') request, channel = self.make_request( "GET", "url_preview?url=http://matrix.org", shorthand=False) request.render(self.preview_url) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( (b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b"Content-Type: text/html; charset=\"utf8\"\r\n\r\n") % (len(end_content), ) + end_content) self.pump() self.assertEqual(channel.code, 200) self.assertEqual(channel.json_body["og:title"], u"\u0434\u043a\u0430")
def test_blacklisted_ip_range_whitelisted_ip(self): """ Blacklisted but then subsequently whitelisted IP addresses can be spidered. """ self.lookups["example.com"] = [(IPv4Address, "1.1.1.1")] channel = self.make_request( "GET", "preview_url?url=http://example.com", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n" % (len(self.end_content),) + self.end_content ) self.pump() self.assertEqual(channel.code, 200) self.assertEqual( channel.json_body, {"og:title": "~matrix~", "og:description": "hi"} )
def test_non_ascii_preview_content_type(self): self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")] end_content = ( b"<html><head>" b'<meta property="og:title" content="\xe4\xea\xe0" />' b'<meta property="og:description" content="hi" />' b"</head></html>" ) channel = self.make_request( "GET", "preview_url?url=http://matrix.org", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( ( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: text/html; charset="windows-1251"\r\n\r\n' ) % (len(end_content),) + end_content ) self.pump() self.assertEqual(channel.code, 200) self.assertEqual(channel.json_body["og:title"], "\u0434\u043a\u0430")
def prepare(self, reactor, clock, hs): db_config = hs.config.database.get_single_database() self.master_store = self.hs.get_datastore() self.storage = hs.get_storage() database = hs.get_datastores().databases[0] self.slaved_store = self.STORE_TYPE( database, make_conn(db_config, database.engine), self.hs) self.event_id = 0 server_factory = ReplicationStreamProtocolFactory(self.hs) self.streamer = hs.get_replication_streamer() # We now do some gut wrenching so that we have a client that is based # off of the slave store rather than the main store. self.replication_handler = ReplicationCommandHandler(self.hs) self.replication_handler._instance_name = "worker" self.replication_handler._replication_data_handler = ReplicationDataHandler( self.slaved_store) client_factory = DirectTcpReplicationClientFactory( self.hs, "client_name", self.replication_handler) client_factory.handler = self.replication_handler server = server_factory.buildProtocol(None) client = client_factory.buildProtocol(None) client.makeConnection(FakeTransport(server, reactor)) self.server_to_client_transport = FakeTransport(client, reactor) server.makeConnection(self.server_to_client_transport)
def _handle_http_replication_attempt(self, hs, repl_port): """Handles a connection attempt to the given HS replication HTTP listener on the given port. """ # We should have at least one outbound connection attempt, where the # last is one to the HTTP repication IP/port. clients = self.reactor.tcpClients self.assertGreaterEqual(len(clients), 1) (host, port, client_factory, _timeout, _bindAddress) = clients.pop() self.assertEqual(host, "1.2.3.4") self.assertEqual(port, repl_port) # Set up client side protocol client_address = IPv4Address("TCP", "127.0.0.1", 1234) client_protocol = client_factory.buildProtocol(("127.0.0.1", 1234)) # Set up the server side protocol server_address = IPv4Address("TCP", host, port) channel = self._hs_to_site[hs].buildProtocol((host, port)) # Connect client to server and vice versa. client_to_server_transport = FakeTransport(channel, self.reactor, client_protocol, server_address, client_address) client_protocol.makeConnection(client_to_server_transport) server_to_client_transport = FakeTransport(client_protocol, self.reactor, channel, client_address, server_address) channel.makeConnection(server_to_client_transport)
def _download_image(self) -> Tuple[str, str]: """Downloads an image into the URL cache. Returns: A (host, media_id) tuple representing the MXC URI of the image. """ self.lookups["cdn.twitter.com"] = [(IPv4Address, "10.1.2.3")] channel = self.make_request( "GET", "preview_url?url=http://cdn.twitter.com/matrixdotorg", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: image/png\r\n\r\n" % (len(SMALL_PNG), ) + SMALL_PNG) self.pump() self.assertEqual(channel.code, 200) body = channel.json_body mxc_uri = body["og:image"] host, _port, media_id = parse_and_validate_mxc_uri(mxc_uri) self.assertIsNone(_port) return host, media_id
def test_ipaddr(self): """ IP addresses can be previewed directly. """ self.lookups["example.com"] = [(IPv4Address, "8.8.8.8")] request, channel = self.make_request( "GET", "url_preview?url=http://example.com", shorthand=False) request.render(self.preview_url) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n" % (len(self.end_content), ) + self.end_content) self.pump() self.assertEqual(channel.code, 200) self.assertEqual(channel.json_body, { "og:title": "~matrix~", "og:description": "hi" })
def test_cache_returns_correct_type(self): self.lookups["matrix.org"] = [(IPv4Address, "8.8.8.8")] request, channel = self.make_request( "GET", "url_preview?url=http://matrix.org", shorthand=False ) request.render(self.preview_url) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n" % (len(self.end_content),) + self.end_content ) self.pump() self.assertEqual(channel.code, 200) self.assertEqual( channel.json_body, {"og:title": "~matrix~", "og:description": "hi"} ) # Check the cache returns the correct response request, channel = self.make_request( "GET", "url_preview?url=http://matrix.org", shorthand=False ) request.render(self.preview_url) self.pump() # Check the cache response has the same content self.assertEqual(channel.code, 200) self.assertEqual( channel.json_body, {"og:title": "~matrix~", "og:description": "hi"} ) # Clear the in-memory cache self.assertIn("http://matrix.org", self.preview_url._cache) self.preview_url._cache.pop("http://matrix.org") self.assertNotIn("http://matrix.org", self.preview_url._cache) # Check the database cache returns the correct response request, channel = self.make_request( "GET", "url_preview?url=http://matrix.org", shorthand=False ) request.render(self.preview_url) self.pump() # Check the cache response has the same content self.assertEqual(channel.code, 200) self.assertEqual( channel.json_body, {"og:title": "~matrix~", "og:description": "hi"} )
def make_worker_hs(self, worker_app: str, extra_config: dict = {}, **kwargs) -> HomeServer: """Make a new worker HS instance, correctly connecting replcation stream to the master HS. Args: worker_app: Type of worker, e.g. `synapse.app.federation_sender`. extra_config: Any extra config to use for this instances. **kwargs: Options that get passed to `self.setup_test_homeserver`, useful to e.g. pass some mocks for things like `http_client` Returns: The new worker HomeServer instance. """ config = self._get_worker_hs_config() config["worker_app"] = worker_app config.update(extra_config) worker_hs = self.setup_test_homeserver( homeserverToUse=GenericWorkerServer, config=config, reactor=self.reactor, **kwargs) store = worker_hs.get_datastore() store.db_pool._db_pool = self.database_pool._db_pool repl_handler = ReplicationCommandHandler(worker_hs) client = ClientReplicationStreamProtocol( worker_hs, "client", "test", self.clock, repl_handler, ) server = self.server_factory.buildProtocol(None) client_transport = FakeTransport(server, self.reactor) client.makeConnection(client_transport) server_transport = FakeTransport(client, self.reactor) server.makeConnection(server_transport) # Set up a resource for the worker resource = ReplicationRestResource(self.hs) for servlet in self.servlets: servlet(worker_hs, resource) self._worker_hs_to_resource[worker_hs] = resource return worker_hs
def reconnect(self): if self._client_transport: self.client.close() if self._server_transport: self.server.close() self._client_transport = FakeTransport(self.server, self.reactor) self.client.makeConnection(self._client_transport) self._server_transport = FakeTransport(self.client, self.reactor) self.server.makeConnection(self._server_transport)
def connect_logging_client(reactor, client_id): # This is essentially tests.server.connect_client, but disabling autoflush on # the client transport. This is necessary to avoid an infinite loop due to # sending of data via the logging transport causing additional logs to be # written. factory = reactor.tcpClients.pop(client_id)[2] client = factory.buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, reactor)) client.makeConnection(FakeTransport(server, reactor, autoflush=False)) return client, server
def prepare(self, reactor, clock, hs): # build a replication server server_factory = ReplicationStreamProtocolFactory(self.hs) self.streamer = server_factory.streamer server = server_factory.buildProtocol(None) # build a replication client, with a dummy handler self.test_handler = TestReplicationClientHandler() self.client = ClientReplicationStreamProtocol("client", "test", clock, self.test_handler) # wire them together self.client.makeConnection(FakeTransport(server, reactor)) server.makeConnection(FakeTransport(self.client, reactor))
def test_oembed_photo(self) -> None: """Test an oEmbed endpoint which returns a 'photo' type which redirects the preview to a new URL.""" self.lookups["publish.twitter.com"] = [(IPv4Address, "10.1.2.3")] self.lookups["cdn.twitter.com"] = [(IPv4Address, "10.1.2.3")] result = { "version": "1.0", "type": "photo", "url": "http://cdn.twitter.com/matrixdotorg", } oembed_content = json.dumps(result).encode("utf-8") channel = self.make_request( "GET", "preview_url?url=http://twitter.com/matrixdotorg/status/12345", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( (b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: application/json; charset="utf8"\r\n\r\n') % (len(oembed_content), ) + oembed_content) self.pump() # Ensure a second request is made to the photo URL. client = self.reactor.tcpClients[1][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived((b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b"Content-Type: image/png\r\n\r\n") % (len(SMALL_PNG), ) + SMALL_PNG) self.pump() # Ensure the URL is what was requested. self.assertIn(b"/matrixdotorg", server.data) self.assertEqual(channel.code, 200) body = channel.json_body self.assertEqual(body["og:url"], "http://twitter.com/matrixdotorg/status/12345") self._assert_small_png(body)
def test_accept_language_config_option(self): """ Accept-Language header is sent to the remote server """ self.lookups["example.com"] = [(IPv4Address, "10.1.2.3")] # Build and make a request to the server channel = self.make_request( "GET", "preview_url?url=http://example.com", shorthand=False, await_result=False, ) self.pump() # Extract Synapse's tcp client client = self.reactor.tcpClients[0][2].buildProtocol(None) # Build a fake remote server to reply with server = AccumulatingProtocol() # Connect the two together server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) # Tell Synapse that it has received some data from the remote server client.dataReceived( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n" % (len(self.end_content),) + self.end_content ) # Move the reactor along until we get a response on our original channel self.pump() self.assertEqual(channel.code, 200) self.assertEqual( channel.json_body, {"og:title": "~matrix~", "og:description": "hi"} ) # Check that the server received the Accept-Language header as part # of the request from Synapse self.assertIn( ( b"Accept-Language: en-UK\r\n" b"Accept-Language: en-US;q=0.9\r\n" b"Accept-Language: fr;q=0.8\r\n" b"Accept-Language: *;q=0.7" ), server.data, )
def test_oembed_rich(self): """Test an oEmbed endpoint which returns HTML content via the 'rich' type.""" # Route the HTTP version to an HTTP endpoint so that the tests work. with patch.dict( "synapse.rest.media.v1.preview_url_resource._oembed_patterns", { re.compile( r"http://twitter\.com/.+/status/.+" ): "http://publish.twitter.com/oembed", }, clear=True, ): self.lookups["publish.twitter.com"] = [(IPv4Address, "10.1.2.3")] result = { "version": "1.0", "type": "rich", "html": "<div>Content Preview</div>", } end_content = json.dumps(result).encode("utf-8") channel = self.make_request( "GET", "preview_url?url=http://twitter.com/matrixdotorg/status/12345", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( ( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: application/json; charset="utf8"\r\n\r\n' ) % (len(end_content),) + end_content ) self.pump() self.assertEqual(channel.code, 200) self.assertEqual( channel.json_body, {"og:title": None, "og:description": "Content Preview"}, )
def test_oembed_format(self): """Test an oEmbed endpoint which requires the format in the URL.""" self.lookups["www.hulu.com"] = [(IPv4Address, "10.1.2.3")] result = { "version": "1.0", "type": "rich", "html": "<div>Content Preview</div>", } end_content = json.dumps(result).encode("utf-8") channel = self.make_request( "GET", "preview_url?url=http://www.hulu.com/watch/12345", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( ( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: application/json; charset="utf8"\r\n\r\n' ) % (len(end_content),) + end_content ) self.pump() # The {format} should have been turned into json. self.assertIn(b"/api/oembed.json", server.data) # A URL parameter of format=json should be provided. self.assertIn(b"format=json", server.data) self.assertEqual(channel.code, 200) body = channel.json_body self.assertEqual( body, { "og:url": "http://www.hulu.com/watch/12345", "og:description": "Content Preview", }, )
def test_lose_connection(self): """ We log the URI correctly redacted when we lose the connection. """ class HangingResource(Resource): """ A Resource that strategically hangs, as if it were processing an answer. """ def render(self, request): return NOT_DONE_YET # Set up a logging handler that we can inspect afterwards output = StringIO() handler = logging.StreamHandler(output) logger.addHandler(handler) old_level = logger.level logger.setLevel(10) self.addCleanup(logger.setLevel, old_level) self.addCleanup(logger.removeHandler, handler) # Make a resource and a Site, the resource will hang and allow us to # time out the request while it's 'processing' base_resource = Resource() base_resource.putChild(b'', HangingResource()) site = SynapseSite("test", "site_tag", {}, base_resource, "1.0") server = site.buildProtocol(None) client = AccumulatingProtocol() client.makeConnection(FakeTransport(server, self.reactor)) server.makeConnection(FakeTransport(client, self.reactor)) # Send a request with an access token that will get redacted server.dataReceived(b"GET /?access_token=bar HTTP/1.0\r\n\r\n") self.pump() # Lose the connection e = Failure(Exception("Failed123")) server.connectionLost(e) handler.flush() # Our access token is redacted and the failure reason is logged. self.assertIn("/?access_token=<redacted>", output.getvalue()) self.assertIn("Failed123", output.getvalue())
def handle_http_replication_attempt(self) -> SynapseRequest: """Asserts that a connection attempt was made to the master HS on the HTTP replication port, then proxies it to the master HS object to be handled. Returns: The request object received by master HS. """ # We should have an outbound connection attempt. 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, 8765) # Set up client side protocol client_protocol = client_factory.buildProtocol(None) # Set up the server side protocol channel = self.site.buildProtocol(None) # hook into the channel's request factory so that we can keep a record # of the requests requests: List[SynapseRequest] = [] real_request_factory = channel.requestFactory def request_factory(*args, **kwargs): request = real_request_factory(*args, **kwargs) requests.append(request) return request channel.requestFactory = request_factory # Connect client to server and vice versa. client_to_server_transport = FakeTransport(channel, self.reactor, client_protocol) client_protocol.makeConnection(client_to_server_transport) server_to_client_transport = FakeTransport(client_protocol, self.reactor, channel) channel.makeConnection(server_to_client_transport) # The request will now be processed by `self.site` and the response # streamed back. self.reactor.advance(0) # We tear down the connection so it doesn't get reused without our # knowledge. server_to_client_transport.loseConnection() client_to_server_transport.loseConnection() # there should have been exactly one request self.assertEqual(len(requests), 1) return requests[0]
def test_oembed_rich(self): """Test an oEmbed endpoint which returns HTML content via the 'rich' type.""" self.lookups["publish.twitter.com"] = [(IPv4Address, "10.1.2.3")] result = { "version": "1.0", "type": "rich", # Note that this provides the author, not the title. "author_name": "Alice", "html": "<div>Content Preview</div>", } end_content = json.dumps(result).encode("utf-8") channel = self.make_request( "GET", "preview_url?url=http://twitter.com/matrixdotorg/status/12345", shorthand=False, await_result=False, ) self.pump() client = self.reactor.tcpClients[0][2].buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( ( b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" b'Content-Type: application/json; charset="utf8"\r\n\r\n' ) % (len(end_content),) + end_content ) self.pump() self.assertEqual(channel.code, 200) body = channel.json_body self.assertEqual( body, { "og:url": "http://twitter.com/matrixdotorg/status/12345", "og:title": "Alice", "og:description": "Content Preview", }, )
def test_reactor(self): """Apply the blacklisting reactor and ensure it properly blocks connections to particular domains and IPs.""" agent = Agent( BlacklistingReactorWrapper( self.reactor, ip_whitelist=self.ip_whitelist, ip_blacklist=self.ip_blacklist, ), ) # The unsafe domains and IPs should be rejected. for domain in (self.unsafe_domain, self.unsafe_ip): self.failureResultOf( agent.request(b"GET", b"http://" + domain), DNSLookupError ) # The safe domains IPs should be accepted. for domain in ( self.safe_domain, self.allowed_domain, self.safe_ip, self.allowed_ip, ): d = agent.request(b"GET", b"http://" + domain) # Grab the latest TCP connection. ( host, port, client_factory, _timeout, _bindAddress, ) = self.reactor.tcpClients[-1] # Make the connection and pump data through it. client = client_factory.buildProtocol(None) server = AccumulatingProtocol() server.makeConnection(FakeTransport(client, self.reactor)) client.makeConnection(FakeTransport(server, self.reactor)) client.dataReceived( b"HTTP/1.0 200 OK\r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n" ) response = self.successResultOf(d) self.assertEqual(response.code, 200)
def _make_connection(self, client_factory, expected_sni): """Builds a test server, and completes the outgoing client connection Returns: HTTPChannel: the test server """ # build the test server server_tls_protocol = _build_test_server(get_connection_factory()) # now, tell the client protocol factory to build the client protocol (it will be a # _WrappingProtocol, around a TLSMemoryBIOProtocol, around an # HTTP11ClientProtocol) and wire the output of said protocol up to the server via # a FakeTransport. # # Normally this would be done by the TCP socket code in Twisted, but we are # stubbing that out here. client_protocol = client_factory.buildProtocol(None) client_protocol.makeConnection( FakeTransport(server_tls_protocol, self.reactor, client_protocol) ) # tell the server tls protocol to send its stuff back to the client, too server_tls_protocol.makeConnection( FakeTransport(client_protocol, self.reactor, server_tls_protocol) ) # grab a hold of the TLS connection, in case it gets torn down server_tls_connection = server_tls_protocol._tlsConnection # fish the test server back out of the server-side TLS protocol. http_protocol = server_tls_protocol.wrappedProtocol # give the reactor a pump to get the TLS juices flowing. self.reactor.pump((0.1,)) # check the SNI server_name = server_tls_connection.get_servername() self.assertEqual( server_name, expected_sni, "Expected SNI %s but got %s" % (expected_sni, server_name), ) return http_protocol
def test_client_sends_body(self): self.cl.post_json("testserv:8008", "foo/bar", timeout=10000, data={"a": "b"}) self.pump() clients = self.reactor.tcpClients self.assertEqual(len(clients), 1) client = clients[0][2].buildProtocol(None) server = HTTPChannel() client.makeConnection(FakeTransport(server, self.reactor)) server.makeConnection(FakeTransport(client, self.reactor)) self.pump(0.1) self.assertEqual(len(server.requests), 1) request = server.requests[0] content = request.content.read() self.assertEqual(content, b'{"a":"b"}')
def test_send_email(self): """Happy-path test that we can send email to a non-TLS server.""" h = self.hs.get_send_email_handler() d = ensureDeferred( h.send_email("*****@*****.**", "test subject", "Tests", "HTML content", "Text content")) # there should be an attempt to connect to localhost:25 self.assertEqual(len(self.reactor.tcpClients), 1) (host, port, client_factory, _timeout, _bindAddress) = self.reactor.tcpClients[0] self.assertEqual(host, "localhost") self.assertEqual(port, 25) # wire it up to an SMTP server message_delivery = _DummyMessageDelivery() server_protocol = smtp.ESMTP() server_protocol.delivery = message_delivery # make sure that the server uses the test reactor to set timeouts server_protocol.callLater = self.reactor.callLater # type: ignore[assignment] client_protocol = client_factory.buildProtocol(None) client_protocol.makeConnection( FakeTransport(server_protocol, self.reactor)) server_protocol.makeConnection( FakeTransport( client_protocol, self.reactor, peer_address=IPv4Address("TCP", "127.0.0.1", 1234), )) # the message should now get delivered self.get_success(d, by=0.1) # check it arrived self.assertEqual(len(message_delivery.messages), 1) user, msg = message_delivery.messages.pop() self.assertEqual(str(user), "*****@*****.**") self.assertIn(b"Subject: test subject", msg)