From f73d4de6918a0925f244a90601bc839925bcc027 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 25 Sep 2020 13:36:33 +0100 Subject: [PATCH] Added typing, docstrings, comments --- synapse/federation/federation_client.py | 50 +++++++++++++------------ synapse/federation/transport/client.py | 10 +++-- synapse/handlers/federation.py | 42 ++++++++++++++++----- synapse/handlers/room_member.py | 36 ++++++++++-------- synapse/handlers/room_member_worker.py | 15 ++++++++ synapse/rest/client/v2_alpha/knock.py | 38 ++++++++++++------- 6 files changed, 124 insertions(+), 67 deletions(-) diff --git a/synapse/federation/federation_client.py b/synapse/federation/federation_client.py index ed524c1d54..063605eaff 100644 --- a/synapse/federation/federation_client.py +++ b/synapse/federation/federation_client.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Copyright 2015, 2016 OpenMarket Ltd # Copyrignt 2020 Sorunome +# Copyrignt 2020 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -880,48 +881,51 @@ class FederationClient(FederationBase): # content. return resp[1] - def send_knock(self, destinations, pdu): - """Sends a knock event to one of a list of homeservers. + async def send_knock(self, destinations: List[str], pdu: EventBase) -> None: + """Attempts to send a knock event to given a list of servers. Iterates + through the list until one attempt succeeds. - Doing so will cause the remote server to add teh event to the graph, + Doing so will cause the remote server to add the event to the graph, and send the event out to the rest of the federation. Args: - destinations (str): Candidate homeservers which are probably participating in the room. - pdu (BaseEvent): event to be sent + destinations: A list of candidate homeservers which are likely to be + participating in the room. + pdu: The event to be sent. - Return: - Deferred: resolves to None. - - Fails with a ``SynapseError`` if the chosen remote server - returns a 300/400 code. - - Fails with a ``RuntimeError`` if no servers were reachable. + Raises: + SynapseError: If the chosen remote server returns a 3xx/4xx code. + RuntimeError: If no servers were reachable. """ - @defer.inlineCallbacks - def send_request(destination): - content = yield self._do_send_knock(destination, pdu) - + async def send_request(destination: str) -> None: + content = await self._do_send_knock(destination, pdu) logger.debug("Got content: %s", content) - return None - return self._try_destination_list("send_knock", destinations, send_request) + return await self._try_destination_list( + "send_knock", destinations, send_request + ) - @defer.inlineCallbacks - def _do_send_knock(self, destination, pdu): + async def _do_send_knock(self, destination: str, pdu: EventBase) -> JsonDict: + """Send a knock event to a remote homeserver. + + Args: + destination: The homeserver to send to. + pdu: The event to send. + + Returns: + The response from the remote homeserver. + """ time_now = self._clock.time_msec() # Only v1 exists! - content = yield self.transport_layer.send_knock_v1( + return await self.transport_layer.send_knock_v1( destination=destination, room_id=pdu.room_id, event_id=pdu.event_id, content=pdu.get_pdu_json(time_now), ) - return content - def get_public_rooms( self, remote_server: str, diff --git a/synapse/federation/transport/client.py b/synapse/federation/transport/client.py index c850a99bbf..15ea981184 100644 --- a/synapse/federation/transport/client.py +++ b/synapse/federation/transport/client.py @@ -2,6 +2,7 @@ # Copyright 2014-2016 OpenMarket Ltd # Copyright 2018 New Vector Ltd # Copyright 2020 Sorunome +# Copyright 2020 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,6 +28,7 @@ from synapse.api.urls import ( FEDERATION_V2_PREFIX, ) from synapse.logging.utils import log_function +from synapse.types import JsonDict logger = logging.getLogger(__name__) @@ -295,15 +297,15 @@ class TransportLayerClient: return response @log_function - async def send_knock_v1(self, destination, room_id, event_id, content): + async def send_knock_v1( + self, destination: str, room_id: str, event_id: str, content: JsonDict, + ): path = _create_v1_path("/send_knock/%s/%s", room_id, event_id) - response = await self.client.put_json( + return await self.client.put_json( destination=destination, path=path, data=content ) - return response - @log_function async def send_invite_v1(self, destination, room_id, event_id, content): path = _create_v1_path("/invite/%s/%s", room_id, event_id) diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index e6aae3a432..c3eb5f63d7 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Copyright 2014-2016 OpenMarket Ltd # Copyright 2017-2018 New Vector Ltd -# Copyright 2019 The Matrix.org Foundation C.I.C. +# Copyright 2019-2020 The Matrix.org Foundation C.I.C. # Copyright 2020 Sorunome # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -1382,31 +1382,53 @@ class FederationHandler(BaseHandler): return True @log_function - async def do_knock(self, target_hosts, room_id, knockee, content): - """ Sends the knock to the remote server. + async def do_knock( + self, target_hosts: List[str], room_id: str, knockee: str, content: JsonDict, + ) -> Tuple[str, int]: + """Sends the knock to the remote server. - This first triggers a /make_knock/ request that returns a partial + This first triggers a /make_knock request that returns a partial event that we can fill out and sign. This is then sent to the - remote server via /send_knock/. + remote server via /send_knock. - Knockees must be signed by the knockee's server before distributing. + Knock events must be signed by the knockee's server before distributing. + + Args: + target_hosts: A list of hosts that we want to try knocking through. + room_id: The ID of the room to knock on. + knockee: The ID of the user who is knocking. + content: The content of the knock event. + + Returns: + A tuple of (event ID, stream ID). + + Raises: + SynapseError: If the chosen remote server returns a 3xx/4xx code. + RuntimeError: If no servers were reachable. """ - logger.debug("Knocking %s to %s", knockee, room_id) + logger.debug("Knocking on room %s on behalf of user %s", room_id, knockee) + # Ask the remote server to create a valid knock event for us. Once received, + # we sign the event origin, event, event_format_version = await self._make_and_verify_event( target_hosts, room_id, knockee, "knock", content, ) - # Try the host that we successfully called /make_knock/ on first for - # the /send_knock/ request. + # Initially try the host that we successfully called /make_knock on try: target_hosts.remove(origin) target_hosts.insert(0, origin) except ValueError: pass + # Send the signed event back to the room await self.federation_client.send_knock(target_hosts, event) - return event + + context = await self.state_handler.compute_event_context(event) + stream_id = await self.persist_events_and_notify( + event.room_id, [(event, context)] + ) + return event.event_id, stream_id async def _handle_queued_pdus(self, room_queue): """Process PDUs which got queued up while we were busy send_joining. diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index cf9b7776dd..789cca5c3e 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -121,17 +121,11 @@ class RoomMemberHandler: @abc.abstractmethod async def _remote_knock( - self, - requester: Requester, - remote_room_hosts: List[str], - room_id: str, - user: UserID, - content: dict, + self, remote_room_hosts: List[str], room_id: str, user: UserID, content: dict, ) -> Tuple[str, int]: """Try and join a room that this server is not in Args: - requester remote_room_hosts: List of servers that can be used to knock via. room_id: Room that we are trying to knock on. user: User who is trying to knock. @@ -576,7 +570,7 @@ class RoomMemberHandler: elif effective_membership_state == Membership.KNOCK: if not is_host_in_room: - # The knock needs to be send over federation + # The knock needs to be sent over federation remote_room_hosts.append(room_id.split(":", 1)[1]) content["membership"] = Membership.KNOCK @@ -587,12 +581,10 @@ class RoomMemberHandler: if "avatar_url" not in content: content["avatar_url"] = await profile.get_avatar_url(target) - remote_knock_response = await self._remote_knock( - requester, remote_room_hosts, room_id, target, content + return await self._remote_knock( + remote_room_hosts, room_id, target, content ) - return remote_knock_response - return await self._local_membership_update( requester=requester, target=target, @@ -1232,7 +1224,20 @@ class RoomMemberMasterHandler(RoomMemberHandler): ) return event.event_id, stream_id - async def _remote_knock(self, requester, remote_room_hosts, room_id, user, content): + async def _remote_knock( + self, remote_room_hosts: List[str], room_id: str, user: UserID, content: dict, + ) -> Tuple[str, int]: + """Sends a knock to a room. Attempts to do so via one remote out of a given list. + + Args: + remote_room_hosts: A list of homeservers to try knocking through. + room_id: The ID of the room to knock on. + user: The user to knock on behalf of. + content: The content of the knock event. + + Returns: + A tuple of (event ID, stream ID). + """ # filter ourselves out of remote_room_hosts remote_room_hosts = [ host for host in remote_room_hosts if host != self.hs.hostname @@ -1241,10 +1246,9 @@ class RoomMemberMasterHandler(RoomMemberHandler): if len(remote_room_hosts) == 0: raise SynapseError(404, "No known servers") - ret = await self.federation_handler.do_knock( - remote_room_hosts, room_id, user.to_string(), content=content, + return await self.federation_handler.do_knock( + remote_room_hosts, room_id, user.to_string(), content=content ) - return ret async def _user_left_room(self, target: UserID, room_id: str) -> None: """Implements RoomMemberHandler._user_left_room diff --git a/synapse/handlers/room_member_worker.py b/synapse/handlers/room_member_worker.py index e7f34737c6..e138b6bad4 100644 --- a/synapse/handlers/room_member_worker.py +++ b/synapse/handlers/room_member_worker.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Copyright 2018 New Vector Ltd +# Copyright 2020 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -79,6 +80,20 @@ class RoomMemberWorkerHandler(RoomMemberHandler): ) return ret["event_id"], ret["stream_id"] + async def _remote_knock( + self, remote_room_hosts: List[str], room_id: str, user: UserID, content: dict, + ) -> Tuple[str, int]: + """Sends a knock to a room. + + Implements RoomMemberHandler._remote_knock + """ + return await self._remote_knock( + remote_room_hosts=remote_room_hosts, + room_id=room_id, + user=user, + content=content, + ) + async def _user_left_room(self, target: UserID, room_id: str) -> None: """Implements RoomMemberHandler._user_left_room """ diff --git a/synapse/rest/client/v2_alpha/knock.py b/synapse/rest/client/v2_alpha/knock.py index 5f4da8d918..de9f9a5b32 100644 --- a/synapse/rest/client/v2_alpha/knock.py +++ b/synapse/rest/client/v2_alpha/knock.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Copyright 2020 Sorunome +# Copyright 2020 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,14 +13,19 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - import logging +from typing import TYPE_CHECKING, List, Optional, Tuple + +from twisted.web.server import Request from synapse.api.errors import SynapseError from synapse.http.servlet import RestServlet, parse_json_object_from_request from synapse.logging.opentracing import set_tag from synapse.rest.client.transactions import HttpTransactionCache -from synapse.types import RoomAlias, RoomID +from synapse.types import JsonDict, RoomAlias, RoomID + +if TYPE_CHECKING: + from synapse.app.homeserver import HomeServer from ._base import client_patterns @@ -27,7 +33,7 @@ logger = logging.getLogger(__name__) class TransactionRestServlet(RestServlet): - def __init__(self, hs): + def __init__(self, hs: "HomeServer"): super(TransactionRestServlet, self).__init__() self.txns = HttpTransactionCache(hs) @@ -39,12 +45,14 @@ class KnockServlet(TransactionRestServlet): PATTERNS = client_patterns("/rooms/(?P[^/]*)/knock") - def __init__(self, hs): - super(KnockServlet, self).__init__(hs) + def __init__(self, hs: "HomeServer"): + super().__init__(hs) self.room_member_handler = hs.get_room_member_handler() self.auth = hs.get_auth() - async def on_POST(self, request, room_id, txn_id=None): + async def on_POST( + self, request: Request, room_id: str, txn_id: Optional[str] = None + ): requester = await self.auth.get_user_by_req(request) content = parse_json_object_from_request(request) @@ -64,7 +72,7 @@ class KnockServlet(TransactionRestServlet): return 200, {} - def on_PUT(self, request, room_id, txn_id): + def on_PUT(self, request: Request, room_id: str, txn_id: str): set_tag("txn_id", txn_id) return self.txns.fetch_or_execute_request( @@ -79,12 +87,14 @@ class KnockRoomAliasServlet(TransactionRestServlet): PATTERNS = client_patterns("/knock/(?P[^/]*)") - def __init__(self, hs): - super(KnockRoomAliasServlet, self).__init__(hs) + def __init__(self, hs: "HomeServer"): + super().__init__(hs) self.room_member_handler = hs.get_room_member_handler() self.auth = hs.get_auth() - async def on_POST(self, request, room_identifier, txn_id=None): + async def on_POST( + self, request: Request, room_identifier: str, txn_id: Optional[str] = None, + ) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request) content = parse_json_object_from_request(request) @@ -97,14 +107,14 @@ class KnockRoomAliasServlet(TransactionRestServlet): try: remote_room_hosts = [ x.decode("ascii") for x in request.args[b"server_name"] - ] + ] # type: Optional[List[str]] except Exception: remote_room_hosts = None elif RoomAlias.is_valid(room_identifier): handler = self.room_member_handler room_alias = RoomAlias.from_string(room_identifier) - room_id, remote_room_hosts = await handler.lookup_room_alias(room_alias) - room_id = room_id.to_string() + room_id_obj, remote_room_hosts = await handler.lookup_room_alias(room_alias) + room_id = room_id_obj.to_string() else: raise SynapseError( 400, "%s was not legal room ID or room alias" % (room_identifier,) @@ -123,7 +133,7 @@ class KnockRoomAliasServlet(TransactionRestServlet): return 200, {} - def on_PUT(self, request, room_identifier, txn_id): + def on_PUT(self, request: Request, room_identifier: str, txn_id: str): set_tag("txn_id", txn_id) return self.txns.fetch_or_execute_request(