summary refs log tree commit diff
path: root/synapse/rest/client/v1
diff options
context:
space:
mode:
authorreivilibre <38398653+reivilibre@users.noreply.github.com>2021-08-17 12:57:58 +0100
committerGitHub <noreply@github.com>2021-08-17 11:57:58 +0000
commit642a42eddece60afbbd5e5a6659fa9b939238b4a (patch)
treefde46b103f636a302d6a47aadf368e19e4fc43da /synapse/rest/client/v1
parentAlways list fallback key types in /sync (#10623) (diff)
downloadsynapse-642a42eddece60afbbd5e5a6659fa9b939238b4a.tar.xz
Flatten the synapse.rest.client package (#10600)
Diffstat (limited to 'synapse/rest/client/v1')
-rw-r--r--synapse/rest/client/v1/__init__.py13
-rw-r--r--synapse/rest/client/v1/directory.py185
-rw-r--r--synapse/rest/client/v1/events.py94
-rw-r--r--synapse/rest/client/v1/initial_sync.py47
-rw-r--r--synapse/rest/client/v1/login.py600
-rw-r--r--synapse/rest/client/v1/logout.py72
-rw-r--r--synapse/rest/client/v1/presence.py95
-rw-r--r--synapse/rest/client/v1/profile.py155
-rw-r--r--synapse/rest/client/v1/push_rule.py354
-rw-r--r--synapse/rest/client/v1/pusher.py171
-rw-r--r--synapse/rest/client/v1/room.py1152
-rw-r--r--synapse/rest/client/v1/voip.py73
12 files changed, 0 insertions, 3011 deletions
diff --git a/synapse/rest/client/v1/__init__.py b/synapse/rest/client/v1/__init__.py
deleted file mode 100644
index 5e83dba2ed..0000000000
--- a/synapse/rest/client/v1/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
diff --git a/synapse/rest/client/v1/directory.py b/synapse/rest/client/v1/directory.py
deleted file mode 100644
index ae92a3df8e..0000000000
--- a/synapse/rest/client/v1/directory.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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 synapse.api.errors import (
-    AuthError,
-    Codes,
-    InvalidClientCredentialsError,
-    NotFoundError,
-    SynapseError,
-)
-from synapse.http.servlet import RestServlet, parse_json_object_from_request
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.types import RoomAlias
-
-logger = logging.getLogger(__name__)
-
-
-def register_servlets(hs, http_server):
-    ClientDirectoryServer(hs).register(http_server)
-    ClientDirectoryListServer(hs).register(http_server)
-    ClientAppserviceDirectoryListServer(hs).register(http_server)
-
-
-class ClientDirectoryServer(RestServlet):
-    PATTERNS = client_patterns("/directory/room/(?P<room_alias>[^/]*)$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.store = hs.get_datastore()
-        self.directory_handler = hs.get_directory_handler()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, room_alias):
-        room_alias = RoomAlias.from_string(room_alias)
-
-        res = await self.directory_handler.get_association(room_alias)
-
-        return 200, res
-
-    async def on_PUT(self, request, room_alias):
-        room_alias = RoomAlias.from_string(room_alias)
-
-        content = parse_json_object_from_request(request)
-        if "room_id" not in content:
-            raise SynapseError(
-                400, 'Missing params: ["room_id"]', errcode=Codes.BAD_JSON
-            )
-
-        logger.debug("Got content: %s", content)
-        logger.debug("Got room name: %s", room_alias.to_string())
-
-        room_id = content["room_id"]
-        servers = content["servers"] if "servers" in content else None
-
-        logger.debug("Got room_id: %s", room_id)
-        logger.debug("Got servers: %s", servers)
-
-        # TODO(erikj): Check types.
-
-        room = await self.store.get_room(room_id)
-        if room is None:
-            raise SynapseError(400, "Room does not exist")
-
-        requester = await self.auth.get_user_by_req(request)
-
-        await self.directory_handler.create_association(
-            requester, room_alias, room_id, servers
-        )
-
-        return 200, {}
-
-    async def on_DELETE(self, request, room_alias):
-        try:
-            service = self.auth.get_appservice_by_req(request)
-            room_alias = RoomAlias.from_string(room_alias)
-            await self.directory_handler.delete_appservice_association(
-                service, room_alias
-            )
-            logger.info(
-                "Application service at %s deleted alias %s",
-                service.url,
-                room_alias.to_string(),
-            )
-            return 200, {}
-        except InvalidClientCredentialsError:
-            # fallback to default user behaviour if they aren't an AS
-            pass
-
-        requester = await self.auth.get_user_by_req(request)
-        user = requester.user
-
-        room_alias = RoomAlias.from_string(room_alias)
-
-        await self.directory_handler.delete_association(requester, room_alias)
-
-        logger.info(
-            "User %s deleted alias %s", user.to_string(), room_alias.to_string()
-        )
-
-        return 200, {}
-
-
-class ClientDirectoryListServer(RestServlet):
-    PATTERNS = client_patterns("/directory/list/room/(?P<room_id>[^/]*)$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.store = hs.get_datastore()
-        self.directory_handler = hs.get_directory_handler()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, room_id):
-        room = await self.store.get_room(room_id)
-        if room is None:
-            raise NotFoundError("Unknown room")
-
-        return 200, {"visibility": "public" if room["is_public"] else "private"}
-
-    async def on_PUT(self, request, room_id):
-        requester = await self.auth.get_user_by_req(request)
-
-        content = parse_json_object_from_request(request)
-        visibility = content.get("visibility", "public")
-
-        await self.directory_handler.edit_published_room_list(
-            requester, room_id, visibility
-        )
-
-        return 200, {}
-
-    async def on_DELETE(self, request, room_id):
-        requester = await self.auth.get_user_by_req(request)
-
-        await self.directory_handler.edit_published_room_list(
-            requester, room_id, "private"
-        )
-
-        return 200, {}
-
-
-class ClientAppserviceDirectoryListServer(RestServlet):
-    PATTERNS = client_patterns(
-        "/directory/list/appservice/(?P<network_id>[^/]*)/(?P<room_id>[^/]*)$", v1=True
-    )
-
-    def __init__(self, hs):
-        super().__init__()
-        self.store = hs.get_datastore()
-        self.directory_handler = hs.get_directory_handler()
-        self.auth = hs.get_auth()
-
-    def on_PUT(self, request, network_id, room_id):
-        content = parse_json_object_from_request(request)
-        visibility = content.get("visibility", "public")
-        return self._edit(request, network_id, room_id, visibility)
-
-    def on_DELETE(self, request, network_id, room_id):
-        return self._edit(request, network_id, room_id, "private")
-
-    async def _edit(self, request, network_id, room_id, visibility):
-        requester = await self.auth.get_user_by_req(request)
-        if not requester.app_service:
-            raise AuthError(
-                403, "Only appservices can edit the appservice published room list"
-            )
-
-        await self.directory_handler.edit_published_appservice_room_list(
-            requester.app_service.id, network_id, room_id, visibility
-        )
-
-        return 200, {}
diff --git a/synapse/rest/client/v1/events.py b/synapse/rest/client/v1/events.py
deleted file mode 100644
index ee7454996e..0000000000
--- a/synapse/rest/client/v1/events.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-"""This module contains REST servlets to do with event streaming, /events."""
-import logging
-
-from synapse.api.errors import SynapseError
-from synapse.http.servlet import RestServlet
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.streams.config import PaginationConfig
-
-logger = logging.getLogger(__name__)
-
-
-class EventStreamRestServlet(RestServlet):
-    PATTERNS = client_patterns("/events$", v1=True)
-
-    DEFAULT_LONGPOLL_TIME_MS = 30000
-
-    def __init__(self, hs):
-        super().__init__()
-        self.event_stream_handler = hs.get_event_stream_handler()
-        self.auth = hs.get_auth()
-        self.store = hs.get_datastore()
-
-    async def on_GET(self, request):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        is_guest = requester.is_guest
-        room_id = None
-        if is_guest:
-            if b"room_id" not in request.args:
-                raise SynapseError(400, "Guest users must specify room_id param")
-        if b"room_id" in request.args:
-            room_id = request.args[b"room_id"][0].decode("ascii")
-
-        pagin_config = await PaginationConfig.from_request(self.store, request)
-        timeout = EventStreamRestServlet.DEFAULT_LONGPOLL_TIME_MS
-        if b"timeout" in request.args:
-            try:
-                timeout = int(request.args[b"timeout"][0])
-            except ValueError:
-                raise SynapseError(400, "timeout must be in milliseconds.")
-
-        as_client_event = b"raw" not in request.args
-
-        chunk = await self.event_stream_handler.get_stream(
-            requester.user.to_string(),
-            pagin_config,
-            timeout=timeout,
-            as_client_event=as_client_event,
-            affect_presence=(not is_guest),
-            room_id=room_id,
-            is_guest=is_guest,
-        )
-
-        return 200, chunk
-
-
-class EventRestServlet(RestServlet):
-    PATTERNS = client_patterns("/events/(?P<event_id>[^/]*)$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.clock = hs.get_clock()
-        self.event_handler = hs.get_event_handler()
-        self.auth = hs.get_auth()
-        self._event_serializer = hs.get_event_client_serializer()
-
-    async def on_GET(self, request, event_id):
-        requester = await self.auth.get_user_by_req(request)
-        event = await self.event_handler.get_event(requester.user, None, event_id)
-
-        time_now = self.clock.time_msec()
-        if event:
-            event = await self._event_serializer.serialize_event(event, time_now)
-            return 200, event
-        else:
-            return 404, "Event not found."
-
-
-def register_servlets(hs, http_server):
-    EventStreamRestServlet(hs).register(http_server)
-    EventRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/initial_sync.py b/synapse/rest/client/v1/initial_sync.py
deleted file mode 100644
index bef1edc838..0000000000
--- a/synapse/rest/client/v1/initial_sync.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-
-from synapse.http.servlet import RestServlet, parse_boolean
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.streams.config import PaginationConfig
-
-
-# TODO: Needs unit testing
-class InitialSyncRestServlet(RestServlet):
-    PATTERNS = client_patterns("/initialSync$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.initial_sync_handler = hs.get_initial_sync_handler()
-        self.auth = hs.get_auth()
-        self.store = hs.get_datastore()
-
-    async def on_GET(self, request):
-        requester = await self.auth.get_user_by_req(request)
-        as_client_event = b"raw" not in request.args
-        pagination_config = await PaginationConfig.from_request(self.store, request)
-        include_archived = parse_boolean(request, "archived", default=False)
-        content = await self.initial_sync_handler.snapshot_all_rooms(
-            user_id=requester.user.to_string(),
-            pagin_config=pagination_config,
-            as_client_event=as_client_event,
-            include_archived=include_archived,
-        )
-
-        return 200, content
-
-
-def register_servlets(hs, http_server):
-    InitialSyncRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/login.py b/synapse/rest/client/v1/login.py
deleted file mode 100644
index 11567bf32c..0000000000
--- a/synapse/rest/client/v1/login.py
+++ /dev/null
@@ -1,600 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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
-import re
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional
-
-from typing_extensions import TypedDict
-
-from synapse.api.errors import Codes, LoginError, SynapseError
-from synapse.api.ratelimiting import Ratelimiter
-from synapse.api.urls import CLIENT_API_PREFIX
-from synapse.appservice import ApplicationService
-from synapse.handlers.sso import SsoIdentityProvider
-from synapse.http import get_request_uri
-from synapse.http.server import HttpServer, finish_request
-from synapse.http.servlet import (
-    RestServlet,
-    assert_params_in_dict,
-    parse_boolean,
-    parse_bytes_from_args,
-    parse_json_object_from_request,
-    parse_string,
-)
-from synapse.http.site import SynapseRequest
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.rest.well_known import WellKnownBuilder
-from synapse.types import JsonDict, UserID
-
-if TYPE_CHECKING:
-    from synapse.server import HomeServer
-
-logger = logging.getLogger(__name__)
-
-
-class LoginResponse(TypedDict, total=False):
-    user_id: str
-    access_token: str
-    home_server: str
-    expires_in_ms: Optional[int]
-    refresh_token: Optional[str]
-    device_id: str
-    well_known: Optional[Dict[str, Any]]
-
-
-class LoginRestServlet(RestServlet):
-    PATTERNS = client_patterns("/login$", v1=True)
-    CAS_TYPE = "m.login.cas"
-    SSO_TYPE = "m.login.sso"
-    TOKEN_TYPE = "m.login.token"
-    JWT_TYPE = "org.matrix.login.jwt"
-    JWT_TYPE_DEPRECATED = "m.login.jwt"
-    APPSERVICE_TYPE = "uk.half-shot.msc2778.login.application_service"
-    REFRESH_TOKEN_PARAM = "org.matrix.msc2918.refresh_token"
-
-    def __init__(self, hs: "HomeServer"):
-        super().__init__()
-        self.hs = hs
-
-        # JWT configuration variables.
-        self.jwt_enabled = hs.config.jwt_enabled
-        self.jwt_secret = hs.config.jwt_secret
-        self.jwt_algorithm = hs.config.jwt_algorithm
-        self.jwt_issuer = hs.config.jwt_issuer
-        self.jwt_audiences = hs.config.jwt_audiences
-
-        # SSO configuration.
-        self.saml2_enabled = hs.config.saml2_enabled
-        self.cas_enabled = hs.config.cas_enabled
-        self.oidc_enabled = hs.config.oidc_enabled
-        self._msc2858_enabled = hs.config.experimental.msc2858_enabled
-        self._msc2918_enabled = hs.config.access_token_lifetime is not None
-
-        self.auth = hs.get_auth()
-
-        self.clock = hs.get_clock()
-
-        self.auth_handler = self.hs.get_auth_handler()
-        self.registration_handler = hs.get_registration_handler()
-        self._sso_handler = hs.get_sso_handler()
-
-        self._well_known_builder = WellKnownBuilder(hs)
-        self._address_ratelimiter = Ratelimiter(
-            store=hs.get_datastore(),
-            clock=hs.get_clock(),
-            rate_hz=self.hs.config.rc_login_address.per_second,
-            burst_count=self.hs.config.rc_login_address.burst_count,
-        )
-        self._account_ratelimiter = Ratelimiter(
-            store=hs.get_datastore(),
-            clock=hs.get_clock(),
-            rate_hz=self.hs.config.rc_login_account.per_second,
-            burst_count=self.hs.config.rc_login_account.burst_count,
-        )
-
-    def on_GET(self, request: SynapseRequest):
-        flows = []
-        if self.jwt_enabled:
-            flows.append({"type": LoginRestServlet.JWT_TYPE})
-            flows.append({"type": LoginRestServlet.JWT_TYPE_DEPRECATED})
-
-        if self.cas_enabled:
-            # we advertise CAS for backwards compat, though MSC1721 renamed it
-            # to SSO.
-            flows.append({"type": LoginRestServlet.CAS_TYPE})
-
-        if self.cas_enabled or self.saml2_enabled or self.oidc_enabled:
-            sso_flow: JsonDict = {
-                "type": LoginRestServlet.SSO_TYPE,
-                "identity_providers": [
-                    _get_auth_flow_dict_for_idp(
-                        idp,
-                    )
-                    for idp in self._sso_handler.get_identity_providers().values()
-                ],
-            }
-
-            if self._msc2858_enabled:
-                # backwards-compatibility support for clients which don't
-                # support the stable API yet
-                sso_flow["org.matrix.msc2858.identity_providers"] = [
-                    _get_auth_flow_dict_for_idp(idp, use_unstable_brands=True)
-                    for idp in self._sso_handler.get_identity_providers().values()
-                ]
-
-            flows.append(sso_flow)
-
-            # While it's valid for us to advertise this login type generally,
-            # synapse currently only gives out these tokens as part of the
-            # SSO login flow.
-            # Generally we don't want to advertise login flows that clients
-            # don't know how to implement, since they (currently) will always
-            # fall back to the fallback API if they don't understand one of the
-            # login flow types returned.
-            flows.append({"type": LoginRestServlet.TOKEN_TYPE})
-
-        flows.extend({"type": t} for t in self.auth_handler.get_supported_login_types())
-
-        flows.append({"type": LoginRestServlet.APPSERVICE_TYPE})
-
-        return 200, {"flows": flows}
-
-    async def on_POST(self, request: SynapseRequest):
-        login_submission = parse_json_object_from_request(request)
-
-        if self._msc2918_enabled:
-            # Check if this login should also issue a refresh token, as per
-            # MSC2918
-            should_issue_refresh_token = parse_boolean(
-                request, name=LoginRestServlet.REFRESH_TOKEN_PARAM, default=False
-            )
-        else:
-            should_issue_refresh_token = False
-
-        try:
-            if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
-                appservice = self.auth.get_appservice_by_req(request)
-
-                if appservice.is_rate_limited():
-                    await self._address_ratelimiter.ratelimit(
-                        None, request.getClientIP()
-                    )
-
-                result = await self._do_appservice_login(
-                    login_submission,
-                    appservice,
-                    should_issue_refresh_token=should_issue_refresh_token,
-                )
-            elif self.jwt_enabled and (
-                login_submission["type"] == LoginRestServlet.JWT_TYPE
-                or login_submission["type"] == LoginRestServlet.JWT_TYPE_DEPRECATED
-            ):
-                await self._address_ratelimiter.ratelimit(None, request.getClientIP())
-                result = await self._do_jwt_login(
-                    login_submission,
-                    should_issue_refresh_token=should_issue_refresh_token,
-                )
-            elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
-                await self._address_ratelimiter.ratelimit(None, request.getClientIP())
-                result = await self._do_token_login(
-                    login_submission,
-                    should_issue_refresh_token=should_issue_refresh_token,
-                )
-            else:
-                await self._address_ratelimiter.ratelimit(None, request.getClientIP())
-                result = await self._do_other_login(
-                    login_submission,
-                    should_issue_refresh_token=should_issue_refresh_token,
-                )
-        except KeyError:
-            raise SynapseError(400, "Missing JSON keys.")
-
-        well_known_data = self._well_known_builder.get_well_known()
-        if well_known_data:
-            result["well_known"] = well_known_data
-        return 200, result
-
-    async def _do_appservice_login(
-        self,
-        login_submission: JsonDict,
-        appservice: ApplicationService,
-        should_issue_refresh_token: bool = False,
-    ):
-        identifier = login_submission.get("identifier")
-        logger.info("Got appservice login request with identifier: %r", identifier)
-
-        if not isinstance(identifier, dict):
-            raise SynapseError(
-                400, "Invalid identifier in login submission", Codes.INVALID_PARAM
-            )
-
-        # this login flow only supports identifiers of type "m.id.user".
-        if identifier.get("type") != "m.id.user":
-            raise SynapseError(
-                400, "Unknown login identifier type", Codes.INVALID_PARAM
-            )
-
-        user = identifier.get("user")
-        if not isinstance(user, str):
-            raise SynapseError(400, "Invalid user in identifier", Codes.INVALID_PARAM)
-
-        if user.startswith("@"):
-            qualified_user_id = user
-        else:
-            qualified_user_id = UserID(user, self.hs.hostname).to_string()
-
-        if not appservice.is_interested_in_user(qualified_user_id):
-            raise LoginError(403, "Invalid access_token", errcode=Codes.FORBIDDEN)
-
-        return await self._complete_login(
-            qualified_user_id,
-            login_submission,
-            ratelimit=appservice.is_rate_limited(),
-            should_issue_refresh_token=should_issue_refresh_token,
-        )
-
-    async def _do_other_login(
-        self, login_submission: JsonDict, should_issue_refresh_token: bool = False
-    ) -> LoginResponse:
-        """Handle non-token/saml/jwt logins
-
-        Args:
-            login_submission:
-            should_issue_refresh_token: True if this login should issue
-                a refresh token alongside the access token.
-
-        Returns:
-            HTTP response
-        """
-        # Log the request we got, but only certain fields to minimise the chance of
-        # logging someone's password (even if they accidentally put it in the wrong
-        # field)
-        logger.info(
-            "Got login request with identifier: %r, medium: %r, address: %r, user: %r",
-            login_submission.get("identifier"),
-            login_submission.get("medium"),
-            login_submission.get("address"),
-            login_submission.get("user"),
-        )
-        canonical_user_id, callback = await self.auth_handler.validate_login(
-            login_submission, ratelimit=True
-        )
-        result = await self._complete_login(
-            canonical_user_id,
-            login_submission,
-            callback,
-            should_issue_refresh_token=should_issue_refresh_token,
-        )
-        return result
-
-    async def _complete_login(
-        self,
-        user_id: str,
-        login_submission: JsonDict,
-        callback: Optional[Callable[[LoginResponse], Awaitable[None]]] = None,
-        create_non_existent_users: bool = False,
-        ratelimit: bool = True,
-        auth_provider_id: Optional[str] = None,
-        should_issue_refresh_token: bool = False,
-    ) -> LoginResponse:
-        """Called when we've successfully authed the user and now need to
-        actually login them in (e.g. create devices). This gets called on
-        all successful logins.
-
-        Applies the ratelimiting for successful login attempts against an
-        account.
-
-        Args:
-            user_id: ID of the user to register.
-            login_submission: Dictionary of login information.
-            callback: Callback function to run after login.
-            create_non_existent_users: Whether to create the user if they don't
-                exist. Defaults to False.
-            ratelimit: Whether to ratelimit the login request.
-            auth_provider_id: The SSO IdP the user used, if any (just used for the
-                prometheus metrics).
-            should_issue_refresh_token: True if this login should issue
-                a refresh token alongside the access token.
-
-        Returns:
-            result: Dictionary of account information after successful login.
-        """
-
-        # Before we actually log them in we check if they've already logged in
-        # too often. This happens here rather than before as we don't
-        # necessarily know the user before now.
-        if ratelimit:
-            await self._account_ratelimiter.ratelimit(None, user_id.lower())
-
-        if create_non_existent_users:
-            canonical_uid = await self.auth_handler.check_user_exists(user_id)
-            if not canonical_uid:
-                canonical_uid = await self.registration_handler.register_user(
-                    localpart=UserID.from_string(user_id).localpart
-                )
-            user_id = canonical_uid
-
-        device_id = login_submission.get("device_id")
-        initial_display_name = login_submission.get("initial_device_display_name")
-        (
-            device_id,
-            access_token,
-            valid_until_ms,
-            refresh_token,
-        ) = await self.registration_handler.register_device(
-            user_id,
-            device_id,
-            initial_display_name,
-            auth_provider_id=auth_provider_id,
-            should_issue_refresh_token=should_issue_refresh_token,
-        )
-
-        result = LoginResponse(
-            user_id=user_id,
-            access_token=access_token,
-            home_server=self.hs.hostname,
-            device_id=device_id,
-        )
-
-        if valid_until_ms is not None:
-            expires_in_ms = valid_until_ms - self.clock.time_msec()
-            result["expires_in_ms"] = expires_in_ms
-
-        if refresh_token is not None:
-            result["refresh_token"] = refresh_token
-
-        if callback is not None:
-            await callback(result)
-
-        return result
-
-    async def _do_token_login(
-        self, login_submission: JsonDict, should_issue_refresh_token: bool = False
-    ) -> LoginResponse:
-        """
-        Handle the final stage of SSO login.
-
-        Args:
-            login_submission: The JSON request body.
-            should_issue_refresh_token: True if this login should issue
-                a refresh token alongside the access token.
-
-        Returns:
-            The body of the JSON response.
-        """
-        token = login_submission["token"]
-        auth_handler = self.auth_handler
-        res = await auth_handler.validate_short_term_login_token(token)
-
-        return await self._complete_login(
-            res.user_id,
-            login_submission,
-            self.auth_handler._sso_login_callback,
-            auth_provider_id=res.auth_provider_id,
-            should_issue_refresh_token=should_issue_refresh_token,
-        )
-
-    async def _do_jwt_login(
-        self, login_submission: JsonDict, should_issue_refresh_token: bool = False
-    ) -> LoginResponse:
-        token = login_submission.get("token", None)
-        if token is None:
-            raise LoginError(
-                403, "Token field for JWT is missing", errcode=Codes.FORBIDDEN
-            )
-
-        import jwt
-
-        try:
-            payload = jwt.decode(
-                token,
-                self.jwt_secret,
-                algorithms=[self.jwt_algorithm],
-                issuer=self.jwt_issuer,
-                audience=self.jwt_audiences,
-            )
-        except jwt.PyJWTError as e:
-            # A JWT error occurred, return some info back to the client.
-            raise LoginError(
-                403,
-                "JWT validation failed: %s" % (str(e),),
-                errcode=Codes.FORBIDDEN,
-            )
-
-        user = payload.get("sub", None)
-        if user is None:
-            raise LoginError(403, "Invalid JWT", errcode=Codes.FORBIDDEN)
-
-        user_id = UserID(user, self.hs.hostname).to_string()
-        result = await self._complete_login(
-            user_id,
-            login_submission,
-            create_non_existent_users=True,
-            should_issue_refresh_token=should_issue_refresh_token,
-        )
-        return result
-
-
-def _get_auth_flow_dict_for_idp(
-    idp: SsoIdentityProvider, use_unstable_brands: bool = False
-) -> JsonDict:
-    """Return an entry for the login flow dict
-
-    Returns an entry suitable for inclusion in "identity_providers" in the
-    response to GET /_matrix/client/r0/login
-
-    Args:
-        idp: the identity provider to describe
-        use_unstable_brands: whether we should use brand identifiers suitable
-           for the unstable API
-    """
-    e: JsonDict = {"id": idp.idp_id, "name": idp.idp_name}
-    if idp.idp_icon:
-        e["icon"] = idp.idp_icon
-    if idp.idp_brand:
-        e["brand"] = idp.idp_brand
-    # use the stable brand identifier if the unstable identifier isn't defined.
-    if use_unstable_brands and idp.unstable_idp_brand:
-        e["brand"] = idp.unstable_idp_brand
-    return e
-
-
-class RefreshTokenServlet(RestServlet):
-    PATTERNS = client_patterns(
-        "/org.matrix.msc2918.refresh_token/refresh$", releases=(), unstable=True
-    )
-
-    def __init__(self, hs: "HomeServer"):
-        self._auth_handler = hs.get_auth_handler()
-        self._clock = hs.get_clock()
-        self.access_token_lifetime = hs.config.access_token_lifetime
-
-    async def on_POST(
-        self,
-        request: SynapseRequest,
-    ):
-        refresh_submission = parse_json_object_from_request(request)
-
-        assert_params_in_dict(refresh_submission, ["refresh_token"])
-        token = refresh_submission["refresh_token"]
-        if not isinstance(token, str):
-            raise SynapseError(400, "Invalid param: refresh_token", Codes.INVALID_PARAM)
-
-        valid_until_ms = self._clock.time_msec() + self.access_token_lifetime
-        access_token, refresh_token = await self._auth_handler.refresh_token(
-            token, valid_until_ms
-        )
-        expires_in_ms = valid_until_ms - self._clock.time_msec()
-        return (
-            200,
-            {
-                "access_token": access_token,
-                "refresh_token": refresh_token,
-                "expires_in_ms": expires_in_ms,
-            },
-        )
-
-
-class SsoRedirectServlet(RestServlet):
-    PATTERNS = list(client_patterns("/login/(cas|sso)/redirect$", v1=True)) + [
-        re.compile(
-            "^"
-            + CLIENT_API_PREFIX
-            + "/r0/login/sso/redirect/(?P<idp_id>[A-Za-z0-9_.~-]+)$"
-        )
-    ]
-
-    def __init__(self, hs: "HomeServer"):
-        # make sure that the relevant handlers are instantiated, so that they
-        # register themselves with the main SSOHandler.
-        if hs.config.cas_enabled:
-            hs.get_cas_handler()
-        if hs.config.saml2_enabled:
-            hs.get_saml_handler()
-        if hs.config.oidc_enabled:
-            hs.get_oidc_handler()
-        self._sso_handler = hs.get_sso_handler()
-        self._msc2858_enabled = hs.config.experimental.msc2858_enabled
-        self._public_baseurl = hs.config.public_baseurl
-
-    def register(self, http_server: HttpServer) -> None:
-        super().register(http_server)
-        if self._msc2858_enabled:
-            # expose additional endpoint for MSC2858 support: backwards-compat support
-            # for clients which don't yet support the stable endpoints.
-            http_server.register_paths(
-                "GET",
-                client_patterns(
-                    "/org.matrix.msc2858/login/sso/redirect/(?P<idp_id>[A-Za-z0-9_.~-]+)$",
-                    releases=(),
-                    unstable=True,
-                ),
-                self.on_GET,
-                self.__class__.__name__,
-            )
-
-    async def on_GET(
-        self, request: SynapseRequest, idp_id: Optional[str] = None
-    ) -> None:
-        if not self._public_baseurl:
-            raise SynapseError(400, "SSO requires a valid public_baseurl")
-
-        # if this isn't the expected hostname, redirect to the right one, so that we
-        # get our cookies back.
-        requested_uri = get_request_uri(request)
-        baseurl_bytes = self._public_baseurl.encode("utf-8")
-        if not requested_uri.startswith(baseurl_bytes):
-            # swap out the incorrect base URL for the right one.
-            #
-            # The idea here is to redirect from
-            #    https://foo.bar/whatever/_matrix/...
-            # to
-            #    https://public.baseurl/_matrix/...
-            #
-            i = requested_uri.index(b"/_matrix")
-            new_uri = baseurl_bytes[:-1] + requested_uri[i:]
-            logger.info(
-                "Requested URI %s is not canonical: redirecting to %s",
-                requested_uri.decode("utf-8", errors="replace"),
-                new_uri.decode("utf-8", errors="replace"),
-            )
-            request.redirect(new_uri)
-            finish_request(request)
-            return
-
-        args: Dict[bytes, List[bytes]] = request.args  # type: ignore
-        client_redirect_url = parse_bytes_from_args(args, "redirectUrl", required=True)
-        sso_url = await self._sso_handler.handle_redirect_request(
-            request,
-            client_redirect_url,
-            idp_id,
-        )
-        logger.info("Redirecting to %s", sso_url)
-        request.redirect(sso_url)
-        finish_request(request)
-
-
-class CasTicketServlet(RestServlet):
-    PATTERNS = client_patterns("/login/cas/ticket", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self._cas_handler = hs.get_cas_handler()
-
-    async def on_GET(self, request: SynapseRequest) -> None:
-        client_redirect_url = parse_string(request, "redirectUrl")
-        ticket = parse_string(request, "ticket", required=True)
-
-        # Maybe get a session ID (if this ticket is from user interactive
-        # authentication).
-        session = parse_string(request, "session")
-
-        # Either client_redirect_url or session must be provided.
-        if not client_redirect_url and not session:
-            message = "Missing string query parameter redirectUrl or session"
-            raise SynapseError(400, message, errcode=Codes.MISSING_PARAM)
-
-        await self._cas_handler.handle_ticket(
-            request, ticket, client_redirect_url, session
-        )
-
-
-def register_servlets(hs, http_server):
-    LoginRestServlet(hs).register(http_server)
-    if hs.config.access_token_lifetime is not None:
-        RefreshTokenServlet(hs).register(http_server)
-    SsoRedirectServlet(hs).register(http_server)
-    if hs.config.cas_enabled:
-        CasTicketServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/logout.py b/synapse/rest/client/v1/logout.py
deleted file mode 100644
index 5aa7908d73..0000000000
--- a/synapse/rest/client/v1/logout.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright 2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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 synapse.http.servlet import RestServlet
-from synapse.rest.client.v2_alpha._base import client_patterns
-
-logger = logging.getLogger(__name__)
-
-
-class LogoutRestServlet(RestServlet):
-    PATTERNS = client_patterns("/logout$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.auth = hs.get_auth()
-        self._auth_handler = hs.get_auth_handler()
-        self._device_handler = hs.get_device_handler()
-
-    async def on_POST(self, request):
-        requester = await self.auth.get_user_by_req(request, allow_expired=True)
-
-        if requester.device_id is None:
-            # The access token wasn't associated with a device.
-            # Just delete the access token
-            access_token = self.auth.get_access_token_from_request(request)
-            await self._auth_handler.delete_access_token(access_token)
-        else:
-            await self._device_handler.delete_device(
-                requester.user.to_string(), requester.device_id
-            )
-
-        return 200, {}
-
-
-class LogoutAllRestServlet(RestServlet):
-    PATTERNS = client_patterns("/logout/all$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.auth = hs.get_auth()
-        self._auth_handler = hs.get_auth_handler()
-        self._device_handler = hs.get_device_handler()
-
-    async def on_POST(self, request):
-        requester = await self.auth.get_user_by_req(request, allow_expired=True)
-        user_id = requester.user.to_string()
-
-        # first delete all of the user's devices
-        await self._device_handler.delete_all_devices_for_user(user_id)
-
-        # .. and then delete any access tokens which weren't associated with
-        # devices.
-        await self._auth_handler.delete_access_tokens_for_user(user_id)
-        return 200, {}
-
-
-def register_servlets(hs, http_server):
-    LogoutRestServlet(hs).register(http_server)
-    LogoutAllRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/presence.py b/synapse/rest/client/v1/presence.py
deleted file mode 100644
index 2b24fe5aa6..0000000000
--- a/synapse/rest/client/v1/presence.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-""" This module contains REST servlets to do with presence: /presence/<paths>
-"""
-import logging
-
-from synapse.api.errors import AuthError, SynapseError
-from synapse.handlers.presence import format_user_presence_state
-from synapse.http.servlet import RestServlet, parse_json_object_from_request
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.types import UserID
-
-logger = logging.getLogger(__name__)
-
-
-class PresenceStatusRestServlet(RestServlet):
-    PATTERNS = client_patterns("/presence/(?P<user_id>[^/]*)/status", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.presence_handler = hs.get_presence_handler()
-        self.clock = hs.get_clock()
-        self.auth = hs.get_auth()
-
-        self._use_presence = hs.config.server.use_presence
-
-    async def on_GET(self, request, user_id):
-        requester = await self.auth.get_user_by_req(request)
-        user = UserID.from_string(user_id)
-
-        if not self._use_presence:
-            return 200, {"presence": "offline"}
-
-        if requester.user != user:
-            allowed = await self.presence_handler.is_visible(
-                observed_user=user, observer_user=requester.user
-            )
-
-            if not allowed:
-                raise AuthError(403, "You are not allowed to see their presence.")
-
-        state = await self.presence_handler.get_state(target_user=user)
-        state = format_user_presence_state(
-            state, self.clock.time_msec(), include_user_id=False
-        )
-
-        return 200, state
-
-    async def on_PUT(self, request, user_id):
-        requester = await self.auth.get_user_by_req(request)
-        user = UserID.from_string(user_id)
-
-        if requester.user != user:
-            raise AuthError(403, "Can only set your own presence state")
-
-        state = {}
-
-        content = parse_json_object_from_request(request)
-
-        try:
-            state["presence"] = content.pop("presence")
-
-            if "status_msg" in content:
-                state["status_msg"] = content.pop("status_msg")
-                if not isinstance(state["status_msg"], str):
-                    raise SynapseError(400, "status_msg must be a string.")
-
-            if content:
-                raise KeyError()
-        except SynapseError as e:
-            raise e
-        except Exception:
-            raise SynapseError(400, "Unable to parse state")
-
-        if self._use_presence:
-            await self.presence_handler.set_state(user, state)
-
-        return 200, {}
-
-
-def register_servlets(hs, http_server):
-    PresenceStatusRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/profile.py b/synapse/rest/client/v1/profile.py
deleted file mode 100644
index f42f4b3567..0000000000
--- a/synapse/rest/client/v1/profile.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-""" This module contains REST servlets to do with profile: /profile/<paths> """
-
-from synapse.api.errors import Codes, SynapseError
-from synapse.http.servlet import RestServlet, parse_json_object_from_request
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.types import UserID
-
-
-class ProfileDisplaynameRestServlet(RestServlet):
-    PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/displayname", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.profile_handler = hs.get_profile_handler()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, user_id):
-        requester_user = None
-
-        if self.hs.config.require_auth_for_profile_requests:
-            requester = await self.auth.get_user_by_req(request)
-            requester_user = requester.user
-
-        user = UserID.from_string(user_id)
-
-        await self.profile_handler.check_profile_query_allowed(user, requester_user)
-
-        displayname = await self.profile_handler.get_displayname(user)
-
-        ret = {}
-        if displayname is not None:
-            ret["displayname"] = displayname
-
-        return 200, ret
-
-    async def on_PUT(self, request, user_id):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        user = UserID.from_string(user_id)
-        is_admin = await self.auth.is_server_admin(requester.user)
-
-        content = parse_json_object_from_request(request)
-
-        try:
-            new_name = content["displayname"]
-        except Exception:
-            raise SynapseError(
-                code=400,
-                msg="Unable to parse name",
-                errcode=Codes.BAD_JSON,
-            )
-
-        await self.profile_handler.set_displayname(user, requester, new_name, is_admin)
-
-        return 200, {}
-
-
-class ProfileAvatarURLRestServlet(RestServlet):
-    PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/avatar_url", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.profile_handler = hs.get_profile_handler()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, user_id):
-        requester_user = None
-
-        if self.hs.config.require_auth_for_profile_requests:
-            requester = await self.auth.get_user_by_req(request)
-            requester_user = requester.user
-
-        user = UserID.from_string(user_id)
-
-        await self.profile_handler.check_profile_query_allowed(user, requester_user)
-
-        avatar_url = await self.profile_handler.get_avatar_url(user)
-
-        ret = {}
-        if avatar_url is not None:
-            ret["avatar_url"] = avatar_url
-
-        return 200, ret
-
-    async def on_PUT(self, request, user_id):
-        requester = await self.auth.get_user_by_req(request)
-        user = UserID.from_string(user_id)
-        is_admin = await self.auth.is_server_admin(requester.user)
-
-        content = parse_json_object_from_request(request)
-        try:
-            new_avatar_url = content["avatar_url"]
-        except KeyError:
-            raise SynapseError(
-                400, "Missing key 'avatar_url'", errcode=Codes.MISSING_PARAM
-            )
-
-        await self.profile_handler.set_avatar_url(
-            user, requester, new_avatar_url, is_admin
-        )
-
-        return 200, {}
-
-
-class ProfileRestServlet(RestServlet):
-    PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.profile_handler = hs.get_profile_handler()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, user_id):
-        requester_user = None
-
-        if self.hs.config.require_auth_for_profile_requests:
-            requester = await self.auth.get_user_by_req(request)
-            requester_user = requester.user
-
-        user = UserID.from_string(user_id)
-
-        await self.profile_handler.check_profile_query_allowed(user, requester_user)
-
-        displayname = await self.profile_handler.get_displayname(user)
-        avatar_url = await self.profile_handler.get_avatar_url(user)
-
-        ret = {}
-        if displayname is not None:
-            ret["displayname"] = displayname
-        if avatar_url is not None:
-            ret["avatar_url"] = avatar_url
-
-        return 200, ret
-
-
-def register_servlets(hs, http_server):
-    ProfileDisplaynameRestServlet(hs).register(http_server)
-    ProfileAvatarURLRestServlet(hs).register(http_server)
-    ProfileRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/push_rule.py b/synapse/rest/client/v1/push_rule.py
deleted file mode 100644
index be29a0b39e..0000000000
--- a/synapse/rest/client/v1/push_rule.py
+++ /dev/null
@@ -1,354 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-from synapse.api.errors import (
-    NotFoundError,
-    StoreError,
-    SynapseError,
-    UnrecognizedRequestError,
-)
-from synapse.http.servlet import (
-    RestServlet,
-    parse_json_value_from_request,
-    parse_string,
-)
-from synapse.push.baserules import BASE_RULE_IDS, NEW_RULE_IDS
-from synapse.push.clientformat import format_push_rules_for_user
-from synapse.push.rulekinds import PRIORITY_CLASS_MAP
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.storage.push_rule import InconsistentRuleException, RuleNotFoundException
-
-
-class PushRuleRestServlet(RestServlet):
-    PATTERNS = client_patterns("/(?P<path>pushrules/.*)$", v1=True)
-    SLIGHTLY_PEDANTIC_TRAILING_SLASH_ERROR = (
-        "Unrecognised request: You probably wanted a trailing slash"
-    )
-
-    def __init__(self, hs):
-        super().__init__()
-        self.auth = hs.get_auth()
-        self.store = hs.get_datastore()
-        self.notifier = hs.get_notifier()
-        self._is_worker = hs.config.worker_app is not None
-
-        self._users_new_default_push_rules = hs.config.users_new_default_push_rules
-
-    async def on_PUT(self, request, path):
-        if self._is_worker:
-            raise Exception("Cannot handle PUT /push_rules on worker")
-
-        spec = _rule_spec_from_path(path.split("/"))
-        try:
-            priority_class = _priority_class_from_spec(spec)
-        except InvalidRuleException as e:
-            raise SynapseError(400, str(e))
-
-        requester = await self.auth.get_user_by_req(request)
-
-        if "/" in spec["rule_id"] or "\\" in spec["rule_id"]:
-            raise SynapseError(400, "rule_id may not contain slashes")
-
-        content = parse_json_value_from_request(request)
-
-        user_id = requester.user.to_string()
-
-        if "attr" in spec:
-            await self.set_rule_attr(user_id, spec, content)
-            self.notify_user(user_id)
-            return 200, {}
-
-        if spec["rule_id"].startswith("."):
-            # Rule ids starting with '.' are reserved for server default rules.
-            raise SynapseError(400, "cannot add new rule_ids that start with '.'")
-
-        try:
-            (conditions, actions) = _rule_tuple_from_request_object(
-                spec["template"], spec["rule_id"], content
-            )
-        except InvalidRuleException as e:
-            raise SynapseError(400, str(e))
-
-        before = parse_string(request, "before")
-        if before:
-            before = _namespaced_rule_id(spec, before)
-
-        after = parse_string(request, "after")
-        if after:
-            after = _namespaced_rule_id(spec, after)
-
-        try:
-            await self.store.add_push_rule(
-                user_id=user_id,
-                rule_id=_namespaced_rule_id_from_spec(spec),
-                priority_class=priority_class,
-                conditions=conditions,
-                actions=actions,
-                before=before,
-                after=after,
-            )
-            self.notify_user(user_id)
-        except InconsistentRuleException as e:
-            raise SynapseError(400, str(e))
-        except RuleNotFoundException as e:
-            raise SynapseError(400, str(e))
-
-        return 200, {}
-
-    async def on_DELETE(self, request, path):
-        if self._is_worker:
-            raise Exception("Cannot handle DELETE /push_rules on worker")
-
-        spec = _rule_spec_from_path(path.split("/"))
-
-        requester = await self.auth.get_user_by_req(request)
-        user_id = requester.user.to_string()
-
-        namespaced_rule_id = _namespaced_rule_id_from_spec(spec)
-
-        try:
-            await self.store.delete_push_rule(user_id, namespaced_rule_id)
-            self.notify_user(user_id)
-            return 200, {}
-        except StoreError as e:
-            if e.code == 404:
-                raise NotFoundError()
-            else:
-                raise
-
-    async def on_GET(self, request, path):
-        requester = await self.auth.get_user_by_req(request)
-        user_id = requester.user.to_string()
-
-        # we build up the full structure and then decide which bits of it
-        # to send which means doing unnecessary work sometimes but is
-        # is probably not going to make a whole lot of difference
-        rules = await self.store.get_push_rules_for_user(user_id)
-
-        rules = format_push_rules_for_user(requester.user, rules)
-
-        path = path.split("/")[1:]
-
-        if path == []:
-            # we're a reference impl: pedantry is our job.
-            raise UnrecognizedRequestError(
-                PushRuleRestServlet.SLIGHTLY_PEDANTIC_TRAILING_SLASH_ERROR
-            )
-
-        if path[0] == "":
-            return 200, rules
-        elif path[0] == "global":
-            result = _filter_ruleset_with_path(rules["global"], path[1:])
-            return 200, result
-        else:
-            raise UnrecognizedRequestError()
-
-    def notify_user(self, user_id):
-        stream_id = self.store.get_max_push_rules_stream_id()
-        self.notifier.on_new_event("push_rules_key", stream_id, users=[user_id])
-
-    async def set_rule_attr(self, user_id, spec, val):
-        if spec["attr"] not in ("enabled", "actions"):
-            # for the sake of potential future expansion, shouldn't report
-            # 404 in the case of an unknown request so check it corresponds to
-            # a known attribute first.
-            raise UnrecognizedRequestError()
-
-        namespaced_rule_id = _namespaced_rule_id_from_spec(spec)
-        rule_id = spec["rule_id"]
-        is_default_rule = rule_id.startswith(".")
-        if is_default_rule:
-            if namespaced_rule_id not in BASE_RULE_IDS:
-                raise NotFoundError("Unknown rule %s" % (namespaced_rule_id,))
-        if spec["attr"] == "enabled":
-            if isinstance(val, dict) and "enabled" in val:
-                val = val["enabled"]
-            if not isinstance(val, bool):
-                # Legacy fallback
-                # This should *actually* take a dict, but many clients pass
-                # bools directly, so let's not break them.
-                raise SynapseError(400, "Value for 'enabled' must be boolean")
-            return await self.store.set_push_rule_enabled(
-                user_id, namespaced_rule_id, val, is_default_rule
-            )
-        elif spec["attr"] == "actions":
-            actions = val.get("actions")
-            _check_actions(actions)
-            namespaced_rule_id = _namespaced_rule_id_from_spec(spec)
-            rule_id = spec["rule_id"]
-            is_default_rule = rule_id.startswith(".")
-            if is_default_rule:
-                if user_id in self._users_new_default_push_rules:
-                    rule_ids = NEW_RULE_IDS
-                else:
-                    rule_ids = BASE_RULE_IDS
-
-                if namespaced_rule_id not in rule_ids:
-                    raise SynapseError(404, "Unknown rule %r" % (namespaced_rule_id,))
-            return await self.store.set_push_rule_actions(
-                user_id, namespaced_rule_id, actions, is_default_rule
-            )
-        else:
-            raise UnrecognizedRequestError()
-
-
-def _rule_spec_from_path(path):
-    """Turn a sequence of path components into a rule spec
-
-    Args:
-        path (sequence[unicode]): the URL path components.
-
-    Returns:
-        dict: rule spec dict, containing scope/template/rule_id entries,
-            and possibly attr.
-
-    Raises:
-        UnrecognizedRequestError if the path components cannot be parsed.
-    """
-    if len(path) < 2:
-        raise UnrecognizedRequestError()
-    if path[0] != "pushrules":
-        raise UnrecognizedRequestError()
-
-    scope = path[1]
-    path = path[2:]
-    if scope != "global":
-        raise UnrecognizedRequestError()
-
-    if len(path) == 0:
-        raise UnrecognizedRequestError()
-
-    template = path[0]
-    path = path[1:]
-
-    if len(path) == 0 or len(path[0]) == 0:
-        raise UnrecognizedRequestError()
-
-    rule_id = path[0]
-
-    spec = {"scope": scope, "template": template, "rule_id": rule_id}
-
-    path = path[1:]
-
-    if len(path) > 0 and len(path[0]) > 0:
-        spec["attr"] = path[0]
-
-    return spec
-
-
-def _rule_tuple_from_request_object(rule_template, rule_id, req_obj):
-    if rule_template in ["override", "underride"]:
-        if "conditions" not in req_obj:
-            raise InvalidRuleException("Missing 'conditions'")
-        conditions = req_obj["conditions"]
-        for c in conditions:
-            if "kind" not in c:
-                raise InvalidRuleException("Condition without 'kind'")
-    elif rule_template == "room":
-        conditions = [{"kind": "event_match", "key": "room_id", "pattern": rule_id}]
-    elif rule_template == "sender":
-        conditions = [{"kind": "event_match", "key": "user_id", "pattern": rule_id}]
-    elif rule_template == "content":
-        if "pattern" not in req_obj:
-            raise InvalidRuleException("Content rule missing 'pattern'")
-        pat = req_obj["pattern"]
-
-        conditions = [{"kind": "event_match", "key": "content.body", "pattern": pat}]
-    else:
-        raise InvalidRuleException("Unknown rule template: %s" % (rule_template,))
-
-    if "actions" not in req_obj:
-        raise InvalidRuleException("No actions found")
-    actions = req_obj["actions"]
-
-    _check_actions(actions)
-
-    return conditions, actions
-
-
-def _check_actions(actions):
-    if not isinstance(actions, list):
-        raise InvalidRuleException("No actions found")
-
-    for a in actions:
-        if a in ["notify", "dont_notify", "coalesce"]:
-            pass
-        elif isinstance(a, dict) and "set_tweak" in a:
-            pass
-        else:
-            raise InvalidRuleException("Unrecognised action")
-
-
-def _filter_ruleset_with_path(ruleset, path):
-    if path == []:
-        raise UnrecognizedRequestError(
-            PushRuleRestServlet.SLIGHTLY_PEDANTIC_TRAILING_SLASH_ERROR
-        )
-
-    if path[0] == "":
-        return ruleset
-    template_kind = path[0]
-    if template_kind not in ruleset:
-        raise UnrecognizedRequestError()
-    path = path[1:]
-    if path == []:
-        raise UnrecognizedRequestError(
-            PushRuleRestServlet.SLIGHTLY_PEDANTIC_TRAILING_SLASH_ERROR
-        )
-    if path[0] == "":
-        return ruleset[template_kind]
-    rule_id = path[0]
-
-    the_rule = None
-    for r in ruleset[template_kind]:
-        if r["rule_id"] == rule_id:
-            the_rule = r
-    if the_rule is None:
-        raise NotFoundError
-
-    path = path[1:]
-    if len(path) == 0:
-        return the_rule
-
-    attr = path[0]
-    if attr in the_rule:
-        # Make sure we return a JSON object as the attribute may be a
-        # JSON value.
-        return {attr: the_rule[attr]}
-    else:
-        raise UnrecognizedRequestError()
-
-
-def _priority_class_from_spec(spec):
-    if spec["template"] not in PRIORITY_CLASS_MAP.keys():
-        raise InvalidRuleException("Unknown template: %s" % (spec["template"]))
-    pc = PRIORITY_CLASS_MAP[spec["template"]]
-
-    return pc
-
-
-def _namespaced_rule_id_from_spec(spec):
-    return _namespaced_rule_id(spec, spec["rule_id"])
-
-
-def _namespaced_rule_id(spec, rule_id):
-    return "global/%s/%s" % (spec["template"], rule_id)
-
-
-class InvalidRuleException(Exception):
-    pass
-
-
-def register_servlets(hs, http_server):
-    PushRuleRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/pusher.py b/synapse/rest/client/v1/pusher.py
deleted file mode 100644
index 18102eca6c..0000000000
--- a/synapse/rest/client/v1/pusher.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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 synapse.api.errors import Codes, StoreError, SynapseError
-from synapse.http.server import respond_with_html_bytes
-from synapse.http.servlet import (
-    RestServlet,
-    assert_params_in_dict,
-    parse_json_object_from_request,
-    parse_string,
-)
-from synapse.push import PusherConfigException
-from synapse.rest.client.v2_alpha._base import client_patterns
-
-logger = logging.getLogger(__name__)
-
-
-class PushersRestServlet(RestServlet):
-    PATTERNS = client_patterns("/pushers$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request):
-        requester = await self.auth.get_user_by_req(request)
-        user = requester.user
-
-        pushers = await self.hs.get_datastore().get_pushers_by_user_id(user.to_string())
-
-        filtered_pushers = [p.as_dict() for p in pushers]
-
-        return 200, {"pushers": filtered_pushers}
-
-
-class PushersSetRestServlet(RestServlet):
-    PATTERNS = client_patterns("/pushers/set$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.auth = hs.get_auth()
-        self.notifier = hs.get_notifier()
-        self.pusher_pool = self.hs.get_pusherpool()
-
-    async def on_POST(self, request):
-        requester = await self.auth.get_user_by_req(request)
-        user = requester.user
-
-        content = parse_json_object_from_request(request)
-
-        if (
-            "pushkey" in content
-            and "app_id" in content
-            and "kind" in content
-            and content["kind"] is None
-        ):
-            await self.pusher_pool.remove_pusher(
-                content["app_id"], content["pushkey"], user_id=user.to_string()
-            )
-            return 200, {}
-
-        assert_params_in_dict(
-            content,
-            [
-                "kind",
-                "app_id",
-                "app_display_name",
-                "device_display_name",
-                "pushkey",
-                "lang",
-                "data",
-            ],
-        )
-
-        logger.debug("set pushkey %s to kind %s", content["pushkey"], content["kind"])
-        logger.debug("Got pushers request with body: %r", content)
-
-        append = False
-        if "append" in content:
-            append = content["append"]
-
-        if not append:
-            await self.pusher_pool.remove_pushers_by_app_id_and_pushkey_not_user(
-                app_id=content["app_id"],
-                pushkey=content["pushkey"],
-                not_user_id=user.to_string(),
-            )
-
-        try:
-            await self.pusher_pool.add_pusher(
-                user_id=user.to_string(),
-                access_token=requester.access_token_id,
-                kind=content["kind"],
-                app_id=content["app_id"],
-                app_display_name=content["app_display_name"],
-                device_display_name=content["device_display_name"],
-                pushkey=content["pushkey"],
-                lang=content["lang"],
-                data=content["data"],
-                profile_tag=content.get("profile_tag", ""),
-            )
-        except PusherConfigException as pce:
-            raise SynapseError(
-                400, "Config Error: " + str(pce), errcode=Codes.MISSING_PARAM
-            )
-
-        self.notifier.on_new_replication_data()
-
-        return 200, {}
-
-
-class PushersRemoveRestServlet(RestServlet):
-    """
-    To allow pusher to be delete by clicking a link (ie. GET request)
-    """
-
-    PATTERNS = client_patterns("/pushers/remove$", v1=True)
-    SUCCESS_HTML = b"<html><body>You have been unsubscribed</body><html>"
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.notifier = hs.get_notifier()
-        self.auth = hs.get_auth()
-        self.pusher_pool = self.hs.get_pusherpool()
-
-    async def on_GET(self, request):
-        requester = await self.auth.get_user_by_req(request, rights="delete_pusher")
-        user = requester.user
-
-        app_id = parse_string(request, "app_id", required=True)
-        pushkey = parse_string(request, "pushkey", required=True)
-
-        try:
-            await self.pusher_pool.remove_pusher(
-                app_id=app_id, pushkey=pushkey, user_id=user.to_string()
-            )
-        except StoreError as se:
-            if se.code != 404:
-                # This is fine: they're already unsubscribed
-                raise
-
-        self.notifier.on_new_replication_data()
-
-        respond_with_html_bytes(
-            request,
-            200,
-            PushersRemoveRestServlet.SUCCESS_HTML,
-        )
-        return None
-
-
-def register_servlets(hs, http_server):
-    PushersRestServlet(hs).register(http_server)
-    PushersSetRestServlet(hs).register(http_server)
-    PushersRemoveRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py
deleted file mode 100644
index ba7250ad8e..0000000000
--- a/synapse/rest/client/v1/room.py
+++ /dev/null
@@ -1,1152 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-# Copyright 2018 New Vector Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-""" This module contains REST servlets to do with rooms: /rooms/<paths> """
-import logging
-import re
-from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
-from urllib import parse as urlparse
-
-from synapse.api.constants import EventTypes, Membership
-from synapse.api.errors import (
-    AuthError,
-    Codes,
-    InvalidClientCredentialsError,
-    MissingClientTokenError,
-    ShadowBanError,
-    SynapseError,
-)
-from synapse.api.filtering import Filter
-from synapse.events.utils import format_event_for_client_v2
-from synapse.http.servlet import (
-    ResolveRoomIdMixin,
-    RestServlet,
-    assert_params_in_dict,
-    parse_boolean,
-    parse_integer,
-    parse_json_object_from_request,
-    parse_string,
-    parse_strings_from_args,
-)
-from synapse.http.site import SynapseRequest
-from synapse.logging.opentracing import set_tag
-from synapse.rest.client.transactions import HttpTransactionCache
-from synapse.rest.client.v2_alpha._base import client_patterns
-from synapse.storage.state import StateFilter
-from synapse.streams.config import PaginationConfig
-from synapse.types import JsonDict, StreamToken, ThirdPartyInstanceID, UserID
-from synapse.util import json_decoder
-from synapse.util.stringutils import parse_and_validate_server_name, random_string
-
-if TYPE_CHECKING:
-    from synapse.server import HomeServer
-
-logger = logging.getLogger(__name__)
-
-
-class TransactionRestServlet(RestServlet):
-    def __init__(self, hs):
-        super().__init__()
-        self.txns = HttpTransactionCache(hs)
-
-
-class RoomCreateRestServlet(TransactionRestServlet):
-    # No PATTERN; we have custom dispatch rules here
-
-    def __init__(self, hs):
-        super().__init__(hs)
-        self._room_creation_handler = hs.get_room_creation_handler()
-        self.auth = hs.get_auth()
-
-    def register(self, http_server):
-        PATTERNS = "/createRoom"
-        register_txn_path(self, PATTERNS, http_server)
-
-    def on_PUT(self, request, txn_id):
-        set_tag("txn_id", txn_id)
-        return self.txns.fetch_or_execute_request(request, self.on_POST, request)
-
-    async def on_POST(self, request):
-        requester = await self.auth.get_user_by_req(request)
-
-        info, _ = await self._room_creation_handler.create_room(
-            requester, self.get_room_config(request)
-        )
-
-        return 200, info
-
-    def get_room_config(self, request):
-        user_supplied_config = parse_json_object_from_request(request)
-        return user_supplied_config
-
-
-# TODO: Needs unit testing for generic events
-class RoomStateEventRestServlet(TransactionRestServlet):
-    def __init__(self, hs):
-        super().__init__(hs)
-        self.event_creation_handler = hs.get_event_creation_handler()
-        self.room_member_handler = hs.get_room_member_handler()
-        self.message_handler = hs.get_message_handler()
-        self.auth = hs.get_auth()
-
-    def register(self, http_server):
-        # /room/$roomid/state/$eventtype
-        no_state_key = "/rooms/(?P<room_id>[^/]*)/state/(?P<event_type>[^/]*)$"
-
-        # /room/$roomid/state/$eventtype/$statekey
-        state_key = (
-            "/rooms/(?P<room_id>[^/]*)/state/"
-            "(?P<event_type>[^/]*)/(?P<state_key>[^/]*)$"
-        )
-
-        http_server.register_paths(
-            "GET",
-            client_patterns(state_key, v1=True),
-            self.on_GET,
-            self.__class__.__name__,
-        )
-        http_server.register_paths(
-            "PUT",
-            client_patterns(state_key, v1=True),
-            self.on_PUT,
-            self.__class__.__name__,
-        )
-        http_server.register_paths(
-            "GET",
-            client_patterns(no_state_key, v1=True),
-            self.on_GET_no_state_key,
-            self.__class__.__name__,
-        )
-        http_server.register_paths(
-            "PUT",
-            client_patterns(no_state_key, v1=True),
-            self.on_PUT_no_state_key,
-            self.__class__.__name__,
-        )
-
-    def on_GET_no_state_key(self, request, room_id, event_type):
-        return self.on_GET(request, room_id, event_type, "")
-
-    def on_PUT_no_state_key(self, request, room_id, event_type):
-        return self.on_PUT(request, room_id, event_type, "")
-
-    async def on_GET(self, request, room_id, event_type, state_key):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        format = parse_string(
-            request, "format", default="content", allowed_values=["content", "event"]
-        )
-
-        msg_handler = self.message_handler
-        data = await msg_handler.get_room_data(
-            user_id=requester.user.to_string(),
-            room_id=room_id,
-            event_type=event_type,
-            state_key=state_key,
-        )
-
-        if not data:
-            raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
-
-        if format == "event":
-            event = format_event_for_client_v2(data.get_dict())
-            return 200, event
-        elif format == "content":
-            return 200, data.get_dict()["content"]
-
-    async def on_PUT(self, request, room_id, event_type, state_key, txn_id=None):
-        requester = await self.auth.get_user_by_req(request)
-
-        if txn_id:
-            set_tag("txn_id", txn_id)
-
-        content = parse_json_object_from_request(request)
-
-        event_dict = {
-            "type": event_type,
-            "content": content,
-            "room_id": room_id,
-            "sender": requester.user.to_string(),
-        }
-
-        if state_key is not None:
-            event_dict["state_key"] = state_key
-
-        try:
-            if event_type == EventTypes.Member:
-                membership = content.get("membership", None)
-                event_id, _ = await self.room_member_handler.update_membership(
-                    requester,
-                    target=UserID.from_string(state_key),
-                    room_id=room_id,
-                    action=membership,
-                    content=content,
-                )
-            else:
-                (
-                    event,
-                    _,
-                ) = await self.event_creation_handler.create_and_send_nonmember_event(
-                    requester, event_dict, txn_id=txn_id
-                )
-                event_id = event.event_id
-        except ShadowBanError:
-            event_id = "$" + random_string(43)
-
-        set_tag("event_id", event_id)
-        ret = {"event_id": event_id}
-        return 200, ret
-
-
-# TODO: Needs unit testing for generic events + feedback
-class RoomSendEventRestServlet(TransactionRestServlet):
-    def __init__(self, hs):
-        super().__init__(hs)
-        self.event_creation_handler = hs.get_event_creation_handler()
-        self.auth = hs.get_auth()
-
-    def register(self, http_server):
-        # /rooms/$roomid/send/$event_type[/$txn_id]
-        PATTERNS = "/rooms/(?P<room_id>[^/]*)/send/(?P<event_type>[^/]*)"
-        register_txn_path(self, PATTERNS, http_server, with_get=True)
-
-    async def on_POST(self, request, room_id, event_type, txn_id=None):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        content = parse_json_object_from_request(request)
-
-        event_dict = {
-            "type": event_type,
-            "content": content,
-            "room_id": room_id,
-            "sender": requester.user.to_string(),
-        }
-
-        if b"ts" in request.args and requester.app_service:
-            event_dict["origin_server_ts"] = parse_integer(request, "ts", 0)
-
-        try:
-            (
-                event,
-                _,
-            ) = await self.event_creation_handler.create_and_send_nonmember_event(
-                requester, event_dict, txn_id=txn_id
-            )
-            event_id = event.event_id
-        except ShadowBanError:
-            event_id = "$" + random_string(43)
-
-        set_tag("event_id", event_id)
-        return 200, {"event_id": event_id}
-
-    def on_GET(self, request, room_id, event_type, txn_id):
-        return 200, "Not implemented"
-
-    def on_PUT(self, request, room_id, event_type, txn_id):
-        set_tag("txn_id", txn_id)
-
-        return self.txns.fetch_or_execute_request(
-            request, self.on_POST, request, room_id, event_type, txn_id
-        )
-
-
-# TODO: Needs unit testing for room ID + alias joins
-class JoinRoomAliasServlet(ResolveRoomIdMixin, TransactionRestServlet):
-    def __init__(self, hs):
-        super().__init__(hs)
-        super(ResolveRoomIdMixin, self).__init__(hs)  # ensure the Mixin is set up
-        self.auth = hs.get_auth()
-
-    def register(self, http_server):
-        # /join/$room_identifier[/$txn_id]
-        PATTERNS = "/join/(?P<room_identifier>[^/]*)"
-        register_txn_path(self, PATTERNS, http_server)
-
-    async def on_POST(
-        self,
-        request: SynapseRequest,
-        room_identifier: str,
-        txn_id: Optional[str] = None,
-    ):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-
-        try:
-            content = parse_json_object_from_request(request)
-        except Exception:
-            # Turns out we used to ignore the body entirely, and some clients
-            # cheekily send invalid bodies.
-            content = {}
-
-        # twisted.web.server.Request.args is incorrectly defined as Optional[Any]
-        args: Dict[bytes, List[bytes]] = request.args  # type: ignore
-        remote_room_hosts = parse_strings_from_args(args, "server_name", required=False)
-        room_id, remote_room_hosts = await self.resolve_room_id(
-            room_identifier,
-            remote_room_hosts,
-        )
-
-        await self.room_member_handler.update_membership(
-            requester=requester,
-            target=requester.user,
-            room_id=room_id,
-            action="join",
-            txn_id=txn_id,
-            remote_room_hosts=remote_room_hosts,
-            content=content,
-            third_party_signed=content.get("third_party_signed", None),
-        )
-
-        return 200, {"room_id": room_id}
-
-    def on_PUT(self, request, room_identifier, txn_id):
-        set_tag("txn_id", txn_id)
-
-        return self.txns.fetch_or_execute_request(
-            request, self.on_POST, request, room_identifier, txn_id
-        )
-
-
-# TODO: Needs unit testing
-class PublicRoomListRestServlet(TransactionRestServlet):
-    PATTERNS = client_patterns("/publicRooms$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__(hs)
-        self.hs = hs
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request):
-        server = parse_string(request, "server")
-
-        try:
-            await self.auth.get_user_by_req(request, allow_guest=True)
-        except InvalidClientCredentialsError as e:
-            # Option to allow servers to require auth when accessing
-            # /publicRooms via CS API. This is especially helpful in private
-            # federations.
-            if not self.hs.config.allow_public_rooms_without_auth:
-                raise
-
-            # We allow people to not be authed if they're just looking at our
-            # room list, but require auth when we proxy the request.
-            # In both cases we call the auth function, as that has the side
-            # effect of logging who issued this request if an access token was
-            # provided.
-            if server:
-                raise e
-
-        limit: Optional[int] = parse_integer(request, "limit", 0)
-        since_token = parse_string(request, "since")
-
-        if limit == 0:
-            # zero is a special value which corresponds to no limit.
-            limit = None
-
-        handler = self.hs.get_room_list_handler()
-        if server and server != self.hs.config.server_name:
-            # Ensure the server is valid.
-            try:
-                parse_and_validate_server_name(server)
-            except ValueError:
-                raise SynapseError(
-                    400,
-                    "Invalid server name: %s" % (server,),
-                    Codes.INVALID_PARAM,
-                )
-
-            data = await handler.get_remote_public_room_list(
-                server, limit=limit, since_token=since_token
-            )
-        else:
-            data = await handler.get_local_public_room_list(
-                limit=limit, since_token=since_token
-            )
-
-        return 200, data
-
-    async def on_POST(self, request):
-        await self.auth.get_user_by_req(request, allow_guest=True)
-
-        server = parse_string(request, "server")
-        content = parse_json_object_from_request(request)
-
-        limit: Optional[int] = int(content.get("limit", 100))
-        since_token = content.get("since", None)
-        search_filter = content.get("filter", None)
-
-        include_all_networks = content.get("include_all_networks", False)
-        third_party_instance_id = content.get("third_party_instance_id", None)
-
-        if include_all_networks:
-            network_tuple = None
-            if third_party_instance_id is not None:
-                raise SynapseError(
-                    400, "Can't use include_all_networks with an explicit network"
-                )
-        elif third_party_instance_id is None:
-            network_tuple = ThirdPartyInstanceID(None, None)
-        else:
-            network_tuple = ThirdPartyInstanceID.from_string(third_party_instance_id)
-
-        if limit == 0:
-            # zero is a special value which corresponds to no limit.
-            limit = None
-
-        handler = self.hs.get_room_list_handler()
-        if server and server != self.hs.config.server_name:
-            # Ensure the server is valid.
-            try:
-                parse_and_validate_server_name(server)
-            except ValueError:
-                raise SynapseError(
-                    400,
-                    "Invalid server name: %s" % (server,),
-                    Codes.INVALID_PARAM,
-                )
-
-            data = await handler.get_remote_public_room_list(
-                server,
-                limit=limit,
-                since_token=since_token,
-                search_filter=search_filter,
-                include_all_networks=include_all_networks,
-                third_party_instance_id=third_party_instance_id,
-            )
-
-        else:
-            data = await handler.get_local_public_room_list(
-                limit=limit,
-                since_token=since_token,
-                search_filter=search_filter,
-                network_tuple=network_tuple,
-            )
-
-        return 200, data
-
-
-# TODO: Needs unit testing
-class RoomMemberListRestServlet(RestServlet):
-    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/members$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.message_handler = hs.get_message_handler()
-        self.auth = hs.get_auth()
-        self.store = hs.get_datastore()
-
-    async def on_GET(self, request, room_id):
-        # TODO support Pagination stream API (limit/tokens)
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        handler = self.message_handler
-
-        # request the state as of a given event, as identified by a stream token,
-        # for consistency with /messages etc.
-        # useful for getting the membership in retrospect as of a given /sync
-        # response.
-        at_token_string = parse_string(request, "at")
-        if at_token_string is None:
-            at_token = None
-        else:
-            at_token = await StreamToken.from_string(self.store, at_token_string)
-
-        # let you filter down on particular memberships.
-        # XXX: this may not be the best shape for this API - we could pass in a filter
-        # instead, except filters aren't currently aware of memberships.
-        # See https://github.com/matrix-org/matrix-doc/issues/1337 for more details.
-        membership = parse_string(request, "membership")
-        not_membership = parse_string(request, "not_membership")
-
-        events = await handler.get_state_events(
-            room_id=room_id,
-            user_id=requester.user.to_string(),
-            at_token=at_token,
-            state_filter=StateFilter.from_types([(EventTypes.Member, None)]),
-        )
-
-        chunk = []
-
-        for event in events:
-            if (membership and event["content"].get("membership") != membership) or (
-                not_membership and event["content"].get("membership") == not_membership
-            ):
-                continue
-            chunk.append(event)
-
-        return 200, {"chunk": chunk}
-
-
-# deprecated in favour of /members?membership=join?
-# except it does custom AS logic and has a simpler return format
-class JoinedRoomMemberListRestServlet(RestServlet):
-    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/joined_members$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.message_handler = hs.get_message_handler()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, room_id):
-        requester = await self.auth.get_user_by_req(request)
-
-        users_with_profile = await self.message_handler.get_joined_members(
-            requester, room_id
-        )
-
-        return 200, {"joined": users_with_profile}
-
-
-# TODO: Needs better unit testing
-class RoomMessageListRestServlet(RestServlet):
-    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/messages$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.pagination_handler = hs.get_pagination_handler()
-        self.auth = hs.get_auth()
-        self.store = hs.get_datastore()
-
-    async def on_GET(self, request, room_id):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        pagination_config = await PaginationConfig.from_request(
-            self.store, request, default_limit=10
-        )
-        as_client_event = b"raw" not in request.args
-        filter_str = parse_string(request, "filter", encoding="utf-8")
-        if filter_str:
-            filter_json = urlparse.unquote(filter_str)
-            event_filter: Optional[Filter] = Filter(json_decoder.decode(filter_json))
-            if (
-                event_filter
-                and event_filter.filter_json.get("event_format", "client")
-                == "federation"
-            ):
-                as_client_event = False
-        else:
-            event_filter = None
-
-        msgs = await self.pagination_handler.get_messages(
-            room_id=room_id,
-            requester=requester,
-            pagin_config=pagination_config,
-            as_client_event=as_client_event,
-            event_filter=event_filter,
-        )
-
-        return 200, msgs
-
-
-# TODO: Needs unit testing
-class RoomStateRestServlet(RestServlet):
-    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/state$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.message_handler = hs.get_message_handler()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, room_id):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        # Get all the current state for this room
-        events = await self.message_handler.get_state_events(
-            room_id=room_id,
-            user_id=requester.user.to_string(),
-            is_guest=requester.is_guest,
-        )
-        return 200, events
-
-
-# TODO: Needs unit testing
-class RoomInitialSyncRestServlet(RestServlet):
-    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/initialSync$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.initial_sync_handler = hs.get_initial_sync_handler()
-        self.auth = hs.get_auth()
-        self.store = hs.get_datastore()
-
-    async def on_GET(self, request, room_id):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        pagination_config = await PaginationConfig.from_request(self.store, request)
-        content = await self.initial_sync_handler.room_initial_sync(
-            room_id=room_id, requester=requester, pagin_config=pagination_config
-        )
-        return 200, content
-
-
-class RoomEventServlet(RestServlet):
-    PATTERNS = client_patterns(
-        "/rooms/(?P<room_id>[^/]*)/event/(?P<event_id>[^/]*)$", v1=True
-    )
-
-    def __init__(self, hs):
-        super().__init__()
-        self.clock = hs.get_clock()
-        self.event_handler = hs.get_event_handler()
-        self._event_serializer = hs.get_event_client_serializer()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, room_id, event_id):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-        try:
-            event = await self.event_handler.get_event(
-                requester.user, room_id, event_id
-            )
-        except AuthError:
-            # This endpoint is supposed to return a 404 when the requester does
-            # not have permission to access the event
-            # https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-r0-rooms-roomid-event-eventid
-            raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
-
-        time_now = self.clock.time_msec()
-        if event:
-            event = await self._event_serializer.serialize_event(event, time_now)
-            return 200, event
-
-        return SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
-
-
-class RoomEventContextServlet(RestServlet):
-    PATTERNS = client_patterns(
-        "/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$", v1=True
-    )
-
-    def __init__(self, hs):
-        super().__init__()
-        self.clock = hs.get_clock()
-        self.room_context_handler = hs.get_room_context_handler()
-        self._event_serializer = hs.get_event_client_serializer()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request, room_id, event_id):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-
-        limit = parse_integer(request, "limit", default=10)
-
-        # picking the API shape for symmetry with /messages
-        filter_str = parse_string(request, "filter", encoding="utf-8")
-        if filter_str:
-            filter_json = urlparse.unquote(filter_str)
-            event_filter: Optional[Filter] = Filter(json_decoder.decode(filter_json))
-        else:
-            event_filter = None
-
-        results = await self.room_context_handler.get_event_context(
-            requester, room_id, event_id, limit, event_filter
-        )
-
-        if not results:
-            raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
-
-        time_now = self.clock.time_msec()
-        results["events_before"] = await self._event_serializer.serialize_events(
-            results["events_before"], time_now
-        )
-        results["event"] = await self._event_serializer.serialize_event(
-            results["event"], time_now
-        )
-        results["events_after"] = await self._event_serializer.serialize_events(
-            results["events_after"], time_now
-        )
-        results["state"] = await self._event_serializer.serialize_events(
-            results["state"],
-            time_now,
-            # No need to bundle aggregations for state events
-            bundle_aggregations=False,
-        )
-
-        return 200, results
-
-
-class RoomForgetRestServlet(TransactionRestServlet):
-    def __init__(self, hs):
-        super().__init__(hs)
-        self.room_member_handler = hs.get_room_member_handler()
-        self.auth = hs.get_auth()
-
-    def register(self, http_server):
-        PATTERNS = "/rooms/(?P<room_id>[^/]*)/forget"
-        register_txn_path(self, PATTERNS, http_server)
-
-    async def on_POST(self, request, room_id, txn_id=None):
-        requester = await self.auth.get_user_by_req(request, allow_guest=False)
-
-        await self.room_member_handler.forget(user=requester.user, room_id=room_id)
-
-        return 200, {}
-
-    def on_PUT(self, request, room_id, txn_id):
-        set_tag("txn_id", txn_id)
-
-        return self.txns.fetch_or_execute_request(
-            request, self.on_POST, request, room_id, txn_id
-        )
-
-
-# TODO: Needs unit testing
-class RoomMembershipRestServlet(TransactionRestServlet):
-    def __init__(self, hs):
-        super().__init__(hs)
-        self.room_member_handler = hs.get_room_member_handler()
-        self.auth = hs.get_auth()
-
-    def register(self, http_server):
-        # /rooms/$roomid/[invite|join|leave]
-        PATTERNS = (
-            "/rooms/(?P<room_id>[^/]*)/"
-            "(?P<membership_action>join|invite|leave|ban|unban|kick)"
-        )
-        register_txn_path(self, PATTERNS, http_server)
-
-    async def on_POST(self, request, room_id, membership_action, txn_id=None):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-
-        if requester.is_guest and membership_action not in {
-            Membership.JOIN,
-            Membership.LEAVE,
-        }:
-            raise AuthError(403, "Guest access not allowed")
-
-        try:
-            content = parse_json_object_from_request(request)
-        except Exception:
-            # Turns out we used to ignore the body entirely, and some clients
-            # cheekily send invalid bodies.
-            content = {}
-
-        if membership_action == "invite" and self._has_3pid_invite_keys(content):
-            try:
-                await self.room_member_handler.do_3pid_invite(
-                    room_id,
-                    requester.user,
-                    content["medium"],
-                    content["address"],
-                    content["id_server"],
-                    requester,
-                    txn_id,
-                    content.get("id_access_token"),
-                )
-            except ShadowBanError:
-                # Pretend the request succeeded.
-                pass
-            return 200, {}
-
-        target = requester.user
-        if membership_action in ["invite", "ban", "unban", "kick"]:
-            assert_params_in_dict(content, ["user_id"])
-            target = UserID.from_string(content["user_id"])
-
-        event_content = None
-        if "reason" in content:
-            event_content = {"reason": content["reason"]}
-
-        try:
-            await self.room_member_handler.update_membership(
-                requester=requester,
-                target=target,
-                room_id=room_id,
-                action=membership_action,
-                txn_id=txn_id,
-                third_party_signed=content.get("third_party_signed", None),
-                content=event_content,
-            )
-        except ShadowBanError:
-            # Pretend the request succeeded.
-            pass
-
-        return_value = {}
-
-        if membership_action == "join":
-            return_value["room_id"] = room_id
-
-        return 200, return_value
-
-    def _has_3pid_invite_keys(self, content):
-        for key in {"id_server", "medium", "address"}:
-            if key not in content:
-                return False
-        return True
-
-    def on_PUT(self, request, room_id, membership_action, txn_id):
-        set_tag("txn_id", txn_id)
-
-        return self.txns.fetch_or_execute_request(
-            request, self.on_POST, request, room_id, membership_action, txn_id
-        )
-
-
-class RoomRedactEventRestServlet(TransactionRestServlet):
-    def __init__(self, hs):
-        super().__init__(hs)
-        self.event_creation_handler = hs.get_event_creation_handler()
-        self.auth = hs.get_auth()
-
-    def register(self, http_server):
-        PATTERNS = "/rooms/(?P<room_id>[^/]*)/redact/(?P<event_id>[^/]*)"
-        register_txn_path(self, PATTERNS, http_server)
-
-    async def on_POST(self, request, room_id, event_id, txn_id=None):
-        requester = await self.auth.get_user_by_req(request)
-        content = parse_json_object_from_request(request)
-
-        try:
-            (
-                event,
-                _,
-            ) = await self.event_creation_handler.create_and_send_nonmember_event(
-                requester,
-                {
-                    "type": EventTypes.Redaction,
-                    "content": content,
-                    "room_id": room_id,
-                    "sender": requester.user.to_string(),
-                    "redacts": event_id,
-                },
-                txn_id=txn_id,
-            )
-            event_id = event.event_id
-        except ShadowBanError:
-            event_id = "$" + random_string(43)
-
-        set_tag("event_id", event_id)
-        return 200, {"event_id": event_id}
-
-    def on_PUT(self, request, room_id, event_id, txn_id):
-        set_tag("txn_id", txn_id)
-
-        return self.txns.fetch_or_execute_request(
-            request, self.on_POST, request, room_id, event_id, txn_id
-        )
-
-
-class RoomTypingRestServlet(RestServlet):
-    PATTERNS = client_patterns(
-        "/rooms/(?P<room_id>[^/]*)/typing/(?P<user_id>[^/]*)$", v1=True
-    )
-
-    def __init__(self, hs: "HomeServer"):
-        super().__init__()
-        self.hs = hs
-        self.presence_handler = hs.get_presence_handler()
-        self.auth = hs.get_auth()
-
-        # If we're not on the typing writer instance we should scream if we get
-        # requests.
-        self._is_typing_writer = (
-            hs.config.worker.writers.typing == hs.get_instance_name()
-        )
-
-    async def on_PUT(self, request, room_id, user_id):
-        requester = await self.auth.get_user_by_req(request)
-
-        if not self._is_typing_writer:
-            raise Exception("Got /typing request on instance that is not typing writer")
-
-        room_id = urlparse.unquote(room_id)
-        target_user = UserID.from_string(urlparse.unquote(user_id))
-
-        content = parse_json_object_from_request(request)
-
-        await self.presence_handler.bump_presence_active_time(requester.user)
-
-        # Limit timeout to stop people from setting silly typing timeouts.
-        timeout = min(content.get("timeout", 30000), 120000)
-
-        # Defer getting the typing handler since it will raise on workers.
-        typing_handler = self.hs.get_typing_writer_handler()
-
-        try:
-            if content["typing"]:
-                await typing_handler.started_typing(
-                    target_user=target_user,
-                    requester=requester,
-                    room_id=room_id,
-                    timeout=timeout,
-                )
-            else:
-                await typing_handler.stopped_typing(
-                    target_user=target_user, requester=requester, room_id=room_id
-                )
-        except ShadowBanError:
-            # Pretend this worked without error.
-            pass
-
-        return 200, {}
-
-
-class RoomAliasListServlet(RestServlet):
-    PATTERNS = [
-        re.compile(
-            r"^/_matrix/client/unstable/org\.matrix\.msc2432"
-            r"/rooms/(?P<room_id>[^/]*)/aliases"
-        ),
-    ] + list(client_patterns("/rooms/(?P<room_id>[^/]*)/aliases$", unstable=False))
-
-    def __init__(self, hs: "HomeServer"):
-        super().__init__()
-        self.auth = hs.get_auth()
-        self.directory_handler = hs.get_directory_handler()
-
-    async def on_GET(self, request, room_id):
-        requester = await self.auth.get_user_by_req(request)
-
-        alias_list = await self.directory_handler.get_aliases_for_room(
-            requester, room_id
-        )
-
-        return 200, {"aliases": alias_list}
-
-
-class SearchRestServlet(RestServlet):
-    PATTERNS = client_patterns("/search$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.search_handler = hs.get_search_handler()
-        self.auth = hs.get_auth()
-
-    async def on_POST(self, request):
-        requester = await self.auth.get_user_by_req(request)
-
-        content = parse_json_object_from_request(request)
-
-        batch = parse_string(request, "next_batch")
-        results = await self.search_handler.search(requester.user, content, batch)
-
-        return 200, results
-
-
-class JoinedRoomsRestServlet(RestServlet):
-    PATTERNS = client_patterns("/joined_rooms$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.store = hs.get_datastore()
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request):
-        requester = await self.auth.get_user_by_req(request, allow_guest=True)
-
-        room_ids = await self.store.get_rooms_for_user(requester.user.to_string())
-        return 200, {"joined_rooms": list(room_ids)}
-
-
-def register_txn_path(servlet, regex_string, http_server, with_get=False):
-    """Registers a transaction-based path.
-
-    This registers two paths:
-        PUT regex_string/$txnid
-        POST regex_string
-
-    Args:
-        regex_string (str): The regex string to register. Must NOT have a
-        trailing $ as this string will be appended to.
-        http_server : The http_server to register paths with.
-        with_get: True to also register respective GET paths for the PUTs.
-    """
-    http_server.register_paths(
-        "POST",
-        client_patterns(regex_string + "$", v1=True),
-        servlet.on_POST,
-        servlet.__class__.__name__,
-    )
-    http_server.register_paths(
-        "PUT",
-        client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
-        servlet.on_PUT,
-        servlet.__class__.__name__,
-    )
-    if with_get:
-        http_server.register_paths(
-            "GET",
-            client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
-            servlet.on_GET,
-            servlet.__class__.__name__,
-        )
-
-
-class RoomSpaceSummaryRestServlet(RestServlet):
-    PATTERNS = (
-        re.compile(
-            "^/_matrix/client/unstable/org.matrix.msc2946"
-            "/rooms/(?P<room_id>[^/]*)/spaces$"
-        ),
-    )
-
-    def __init__(self, hs: "HomeServer"):
-        super().__init__()
-        self._auth = hs.get_auth()
-        self._room_summary_handler = hs.get_room_summary_handler()
-
-    async def on_GET(
-        self, request: SynapseRequest, room_id: str
-    ) -> Tuple[int, JsonDict]:
-        requester = await self._auth.get_user_by_req(request, allow_guest=True)
-
-        max_rooms_per_space = parse_integer(request, "max_rooms_per_space")
-        if max_rooms_per_space is not None and max_rooms_per_space < 0:
-            raise SynapseError(
-                400,
-                "Value for 'max_rooms_per_space' must be a non-negative integer",
-                Codes.BAD_JSON,
-            )
-
-        return 200, await self._room_summary_handler.get_space_summary(
-            requester.user.to_string(),
-            room_id,
-            suggested_only=parse_boolean(request, "suggested_only", default=False),
-            max_rooms_per_space=max_rooms_per_space,
-        )
-
-    # TODO When switching to the stable endpoint, remove the POST handler.
-    async def on_POST(
-        self, request: SynapseRequest, room_id: str
-    ) -> Tuple[int, JsonDict]:
-        requester = await self._auth.get_user_by_req(request, allow_guest=True)
-        content = parse_json_object_from_request(request)
-
-        suggested_only = content.get("suggested_only", False)
-        if not isinstance(suggested_only, bool):
-            raise SynapseError(
-                400, "'suggested_only' must be a boolean", Codes.BAD_JSON
-            )
-
-        max_rooms_per_space = content.get("max_rooms_per_space")
-        if max_rooms_per_space is not None:
-            if not isinstance(max_rooms_per_space, int):
-                raise SynapseError(
-                    400, "'max_rooms_per_space' must be an integer", Codes.BAD_JSON
-                )
-            if max_rooms_per_space < 0:
-                raise SynapseError(
-                    400,
-                    "Value for 'max_rooms_per_space' must be a non-negative integer",
-                    Codes.BAD_JSON,
-                )
-
-        return 200, await self._room_summary_handler.get_space_summary(
-            requester.user.to_string(),
-            room_id,
-            suggested_only=suggested_only,
-            max_rooms_per_space=max_rooms_per_space,
-        )
-
-
-class RoomHierarchyRestServlet(RestServlet):
-    PATTERNS = (
-        re.compile(
-            "^/_matrix/client/unstable/org.matrix.msc2946"
-            "/rooms/(?P<room_id>[^/]*)/hierarchy$"
-        ),
-    )
-
-    def __init__(self, hs: "HomeServer"):
-        super().__init__()
-        self._auth = hs.get_auth()
-        self._room_summary_handler = hs.get_room_summary_handler()
-
-    async def on_GET(
-        self, request: SynapseRequest, room_id: str
-    ) -> Tuple[int, JsonDict]:
-        requester = await self._auth.get_user_by_req(request, allow_guest=True)
-
-        max_depth = parse_integer(request, "max_depth")
-        if max_depth is not None and max_depth < 0:
-            raise SynapseError(
-                400, "'max_depth' must be a non-negative integer", Codes.BAD_JSON
-            )
-
-        limit = parse_integer(request, "limit")
-        if limit is not None and limit <= 0:
-            raise SynapseError(
-                400, "'limit' must be a positive integer", Codes.BAD_JSON
-            )
-
-        return 200, await self._room_summary_handler.get_room_hierarchy(
-            requester.user.to_string(),
-            room_id,
-            suggested_only=parse_boolean(request, "suggested_only", default=False),
-            max_depth=max_depth,
-            limit=limit,
-            from_token=parse_string(request, "from"),
-        )
-
-
-class RoomSummaryRestServlet(ResolveRoomIdMixin, RestServlet):
-    PATTERNS = (
-        re.compile(
-            "^/_matrix/client/unstable/im.nheko.summary"
-            "/rooms/(?P<room_identifier>[^/]*)/summary$"
-        ),
-    )
-
-    def __init__(self, hs: "HomeServer"):
-        super().__init__(hs)
-        self._auth = hs.get_auth()
-        self._room_summary_handler = hs.get_room_summary_handler()
-
-    async def on_GET(
-        self, request: SynapseRequest, room_identifier: str
-    ) -> Tuple[int, JsonDict]:
-        try:
-            requester = await self._auth.get_user_by_req(request, allow_guest=True)
-            requester_user_id: Optional[str] = requester.user.to_string()
-        except MissingClientTokenError:
-            # auth is optional
-            requester_user_id = None
-
-        # twisted.web.server.Request.args is incorrectly defined as Optional[Any]
-        args: Dict[bytes, List[bytes]] = request.args  # type: ignore
-        remote_room_hosts = parse_strings_from_args(args, "via", required=False)
-        room_id, remote_room_hosts = await self.resolve_room_id(
-            room_identifier,
-            remote_room_hosts,
-        )
-
-        return 200, await self._room_summary_handler.get_room_summary(
-            requester_user_id,
-            room_id,
-            remote_room_hosts,
-        )
-
-
-def register_servlets(hs: "HomeServer", http_server, is_worker=False):
-    RoomStateEventRestServlet(hs).register(http_server)
-    RoomMemberListRestServlet(hs).register(http_server)
-    JoinedRoomMemberListRestServlet(hs).register(http_server)
-    RoomMessageListRestServlet(hs).register(http_server)
-    JoinRoomAliasServlet(hs).register(http_server)
-    RoomMembershipRestServlet(hs).register(http_server)
-    RoomSendEventRestServlet(hs).register(http_server)
-    PublicRoomListRestServlet(hs).register(http_server)
-    RoomStateRestServlet(hs).register(http_server)
-    RoomRedactEventRestServlet(hs).register(http_server)
-    RoomTypingRestServlet(hs).register(http_server)
-    RoomEventContextServlet(hs).register(http_server)
-    RoomSpaceSummaryRestServlet(hs).register(http_server)
-    RoomHierarchyRestServlet(hs).register(http_server)
-    if hs.config.experimental.msc3266_enabled:
-        RoomSummaryRestServlet(hs).register(http_server)
-    RoomEventServlet(hs).register(http_server)
-    JoinedRoomsRestServlet(hs).register(http_server)
-    RoomAliasListServlet(hs).register(http_server)
-    SearchRestServlet(hs).register(http_server)
-
-    # Some servlets only get registered for the main process.
-    if not is_worker:
-        RoomCreateRestServlet(hs).register(http_server)
-        RoomForgetRestServlet(hs).register(http_server)
-
-
-def register_deprecated_servlets(hs, http_server):
-    RoomInitialSyncRestServlet(hs).register(http_server)
diff --git a/synapse/rest/client/v1/voip.py b/synapse/rest/client/v1/voip.py
deleted file mode 100644
index c780ffded5..0000000000
--- a/synapse/rest/client/v1/voip.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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 base64
-import hashlib
-import hmac
-
-from synapse.http.servlet import RestServlet
-from synapse.rest.client.v2_alpha._base import client_patterns
-
-
-class VoipRestServlet(RestServlet):
-    PATTERNS = client_patterns("/voip/turnServer$", v1=True)
-
-    def __init__(self, hs):
-        super().__init__()
-        self.hs = hs
-        self.auth = hs.get_auth()
-
-    async def on_GET(self, request):
-        requester = await self.auth.get_user_by_req(
-            request, self.hs.config.turn_allow_guests
-        )
-
-        turnUris = self.hs.config.turn_uris
-        turnSecret = self.hs.config.turn_shared_secret
-        turnUsername = self.hs.config.turn_username
-        turnPassword = self.hs.config.turn_password
-        userLifetime = self.hs.config.turn_user_lifetime
-
-        if turnUris and turnSecret and userLifetime:
-            expiry = (self.hs.get_clock().time_msec() + userLifetime) / 1000
-            username = "%d:%s" % (expiry, requester.user.to_string())
-
-            mac = hmac.new(
-                turnSecret.encode(), msg=username.encode(), digestmod=hashlib.sha1
-            )
-            # We need to use standard padded base64 encoding here
-            # encode_base64 because we need to add the standard padding to get the
-            # same result as the TURN server.
-            password = base64.b64encode(mac.digest()).decode("ascii")
-
-        elif turnUris and turnUsername and turnPassword and userLifetime:
-            username = turnUsername
-            password = turnPassword
-
-        else:
-            return 200, {}
-
-        return (
-            200,
-            {
-                "username": username,
-                "password": password,
-                "ttl": userLifetime / 1000,
-                "uris": turnUris,
-            },
-        )
-
-
-def register_servlets(hs, http_server):
-    VoipRestServlet(hs).register(http_server)