Added typing, docstrings, comments

This commit is contained in:
Andrew Morgan 2020-09-25 13:36:33 +01:00
parent 2fd7cdc1b3
commit f73d4de691
6 changed files with 124 additions and 67 deletions

View file

@ -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,

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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
"""

View file

@ -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<room_id>[^/]*)/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<room_identifier>[^/]*)")
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(