diff --git a/changelog.d/15243.feature b/changelog.d/15243.feature
new file mode 100644
index 0000000000..c45e974c4c
--- /dev/null
+++ b/changelog.d/15243.feature
@@ -0,0 +1 @@
+Add a primitive helper script for listing worker endpoints.
\ No newline at end of file
diff --git a/synapse/_scripts/generate_workers_map.py b/synapse/_scripts/generate_workers_map.py
new file mode 100755
index 0000000000..6c08878523
--- /dev/null
+++ b/synapse/_scripts/generate_workers_map.py
@@ -0,0 +1,302 @@
+#!/usr/bin/env python
+# Copyright 2022-2023 The Matrix.org Foundation C.I.C.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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 argparse
+import logging
+import re
+from collections import defaultdict
+from dataclasses import dataclass
+from typing import Dict, Iterable, Optional, Pattern, Set, Tuple
+
+import yaml
+
+from synapse.config.homeserver import HomeServerConfig
+from synapse.federation.transport.server import (
+ TransportLayerServer,
+ register_servlets as register_federation_servlets,
+)
+from synapse.http.server import HttpServer, ServletCallback
+from synapse.rest import ClientRestResource
+from synapse.rest.key.v2 import RemoteKey
+from synapse.server import HomeServer
+from synapse.storage import DataStore
+
+logger = logging.getLogger("generate_workers_map")
+
+
+class MockHomeserver(HomeServer):
+ DATASTORE_CLASS = DataStore # type: ignore
+
+ def __init__(self, config: HomeServerConfig, worker_app: Optional[str]) -> None:
+ super().__init__(config.server.server_name, config=config)
+ self.config.worker.worker_app = worker_app
+
+
+GROUP_PATTERN = re.compile(r"\(\?P<[^>]+?>(.+?)\)")
+
+
+@dataclass
+class EndpointDescription:
+ """
+ Describes an endpoint and how it should be routed.
+ """
+
+ # The servlet class that handles this endpoint
+ servlet_class: object
+
+ # The category of this endpoint. Is read from the `CATEGORY` constant in the servlet
+ # class.
+ category: Optional[str]
+
+ # TODO:
+ # - does it need to be routed based on a stream writer config?
+ # - does it benefit from any optimised, but optional, routing?
+ # - what 'opinionated synapse worker class' (event_creator, synchrotron, etc) does
+ # it go in?
+
+
+class EnumerationResource(HttpServer):
+ """
+ Accepts servlet registrations for the purposes of building up a description of
+ all endpoints.
+ """
+
+ def __init__(self, is_worker: bool) -> None:
+ self.registrations: Dict[Tuple[str, str], EndpointDescription] = {}
+ self._is_worker = is_worker
+
+ def register_paths(
+ self,
+ method: str,
+ path_patterns: Iterable[Pattern],
+ callback: ServletCallback,
+ servlet_classname: str,
+ ) -> None:
+ # federation servlet callbacks are wrapped, so unwrap them.
+ callback = getattr(callback, "__wrapped__", callback)
+
+ # fish out the servlet class
+ servlet_class = callback.__self__.__class__ # type: ignore
+
+ if self._is_worker and method in getattr(
+ servlet_class, "WORKERS_DENIED_METHODS", ()
+ ):
+ # This endpoint would cause an error if called on a worker, so pretend it
+ # was never registered!
+ return
+
+ sd = EndpointDescription(
+ servlet_class=servlet_class,
+ category=getattr(servlet_class, "CATEGORY", None),
+ )
+
+ for pat in path_patterns:
+ self.registrations[(method, pat.pattern)] = sd
+
+
+def get_registered_paths_for_hs(
+ hs: HomeServer,
+) -> Dict[Tuple[str, str], EndpointDescription]:
+ """
+ Given a homeserver, get all registered endpoints and their descriptions.
+ """
+
+ enumerator = EnumerationResource(is_worker=hs.config.worker.worker_app is not None)
+ ClientRestResource.register_servlets(enumerator, hs)
+ federation_server = TransportLayerServer(hs)
+
+ # we can't use `federation_server.register_servlets` but this line does the
+ # same thing, only it uses this enumerator
+ register_federation_servlets(
+ federation_server.hs,
+ resource=enumerator,
+ ratelimiter=federation_server.ratelimiter,
+ authenticator=federation_server.authenticator,
+ servlet_groups=federation_server.servlet_groups,
+ )
+
+ # the key server endpoints are separate again
+ RemoteKey(hs).register(enumerator)
+
+ return enumerator.registrations
+
+
+def get_registered_paths_for_default(
+ worker_app: Optional[str], base_config: HomeServerConfig
+) -> Dict[Tuple[str, str], EndpointDescription]:
+ """
+ Given the name of a worker application and a base homeserver configuration,
+ returns:
+
+ Dict from (method, path) to EndpointDescription
+
+ TODO Don't require passing in a config
+ """
+
+ hs = MockHomeserver(base_config, worker_app)
+ # TODO We only do this to avoid an error, but don't need the database etc
+ hs.setup()
+ return get_registered_paths_for_hs(hs)
+
+
+def elide_http_methods_if_unconflicting(
+ registrations: Dict[Tuple[str, str], EndpointDescription],
+ all_possible_registrations: Dict[Tuple[str, str], EndpointDescription],
+) -> Dict[Tuple[str, str], EndpointDescription]:
+ """
+ Elides HTTP methods (by replacing them with `*`) if all possible registered methods
+ can be handled by the worker whose registration map is `registrations`.
+
+ i.e. the only endpoints left with methods (other than `*`) should be the ones where
+ the worker can't handle all possible methods for that path.
+ """
+
+ def paths_to_methods_dict(
+ methods_and_paths: Iterable[Tuple[str, str]]
+ ) -> Dict[str, Set[str]]:
+ """
+ Given (method, path) pairs, produces a dict from path to set of methods
+ available at that path.
+ """
+ result: Dict[str, Set[str]] = {}
+ for method, path in methods_and_paths:
+ result.setdefault(path, set()).add(method)
+ return result
+
+ all_possible_reg_methods = paths_to_methods_dict(all_possible_registrations)
+ reg_methods = paths_to_methods_dict(registrations)
+
+ output = {}
+
+ for path, handleable_methods in reg_methods.items():
+ if handleable_methods == all_possible_reg_methods[path]:
+ any_method = next(iter(handleable_methods))
+ # TODO This assumes that all methods have the same servlet.
+ # I suppose that's possibly dubious?
+ output[("*", path)] = registrations[(any_method, path)]
+ else:
+ for method in handleable_methods:
+ output[(method, path)] = registrations[(method, path)]
+
+ return output
+
+
+def simplify_path_regexes(
+ registrations: Dict[Tuple[str, str], EndpointDescription]
+) -> Dict[Tuple[str, str], EndpointDescription]:
+ """
+ Simplify all the path regexes for the dict of endpoint descriptions,
+ so that we don't use the Python-specific regex extensions
+ (and also to remove needlessly specific detail).
+ """
+
+ def simplify_path_regex(path: str) -> str:
+ """
+ Given a regex pattern, replaces all named capturing groups (e.g. `(?P<blah>xyz)`)
+ with a simpler version available in more common regex dialects (e.g. `.*`).
+ """
+
+ # TODO it's hard to choose between these two;
+ # `.*` is a vague simplification
+ # return GROUP_PATTERN.sub(r"\1", path)
+ return GROUP_PATTERN.sub(r".*", path)
+
+ return {(m, simplify_path_regex(p)): v for (m, p), v in registrations.items()}
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(
+ description=(
+ "Updates a synapse database to the latest schema and optionally runs background updates"
+ " on it."
+ )
+ )
+ parser.add_argument("-v", action="store_true")
+ parser.add_argument(
+ "--config-path",
+ type=argparse.FileType("r"),
+ required=True,
+ help="Synapse configuration file",
+ )
+
+ args = parser.parse_args()
+
+ # TODO
+ # logging.basicConfig(**logging_config)
+
+ # Load, process and sanity-check the config.
+ hs_config = yaml.safe_load(args.config_path)
+
+ config = HomeServerConfig()
+ config.parse_config_dict(hs_config, "", "")
+
+ master_paths = get_registered_paths_for_default(None, config)
+ worker_paths = get_registered_paths_for_default(
+ "synapse.app.generic_worker", config
+ )
+
+ all_paths = {**master_paths, **worker_paths}
+
+ elided_worker_paths = elide_http_methods_if_unconflicting(worker_paths, all_paths)
+ elide_http_methods_if_unconflicting(master_paths, all_paths)
+
+ # TODO SSO endpoints (pick_idp etc) NOT REGISTERED BY THIS SCRIPT
+
+ categories_to_methods_and_paths: Dict[
+ Optional[str], Dict[Tuple[str, str], EndpointDescription]
+ ] = defaultdict(dict)
+
+ for (method, path), desc in elided_worker_paths.items():
+ categories_to_methods_and_paths[desc.category][method, path] = desc
+
+ for category, contents in categories_to_methods_and_paths.items():
+ print_category(category, contents)
+
+
+def print_category(
+ category_name: Optional[str],
+ elided_worker_paths: Dict[Tuple[str, str], EndpointDescription],
+) -> None:
+ """
+ Prints out a category, in documentation page style.
+
+ Example:
+ ```
+ # Category name
+ /path/xyz
+
+ GET /path/abc
+ ```
+ """
+
+ if category_name:
+ print(f"# {category_name}")
+ else:
+ print("# (Uncategorised requests)")
+
+ for ln in sorted(
+ p for m, p in simplify_path_regexes(elided_worker_paths) if m == "*"
+ ):
+ print(ln)
+ print()
+ for ln in sorted(
+ f"{m:6} {p}" for m, p in simplify_path_regexes(elided_worker_paths) if m != "*"
+ ):
+ print(ln)
+ print()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/synapse/federation/transport/server/__init__.py b/synapse/federation/transport/server/__init__.py
index 2725f53cf6..753372fc54 100644
--- a/synapse/federation/transport/server/__init__.py
+++ b/synapse/federation/transport/server/__init__.py
@@ -108,6 +108,7 @@ class PublicRoomList(BaseFederationServlet):
"""
PATH = "/publicRooms"
+ CATEGORY = "Federation requests"
def __init__(
self,
@@ -212,6 +213,7 @@ class OpenIdUserInfo(BaseFederationServlet):
"""
PATH = "/openid/userinfo"
+ CATEGORY = "Federation requests"
REQUIRE_AUTH = False
diff --git a/synapse/federation/transport/server/federation.py b/synapse/federation/transport/server/federation.py
index f7ca87adc4..ec5b5eeafa 100644
--- a/synapse/federation/transport/server/federation.py
+++ b/synapse/federation/transport/server/federation.py
@@ -70,6 +70,7 @@ class BaseFederationServerServlet(BaseFederationServlet):
class FederationSendServlet(BaseFederationServerServlet):
PATH = "/send/(?P<transaction_id>[^/]*)/?"
+ CATEGORY = "Inbound federation transaction request"
# We ratelimit manually in the handler as we queue up the requests and we
# don't want to fill up the ratelimiter with blocked requests.
@@ -138,6 +139,7 @@ class FederationSendServlet(BaseFederationServerServlet):
class FederationEventServlet(BaseFederationServerServlet):
PATH = "/event/(?P<event_id>[^/]*)/?"
+ CATEGORY = "Federation requests"
# This is when someone asks for a data item for a given server data_id pair.
async def on_GET(
@@ -152,6 +154,7 @@ class FederationEventServlet(BaseFederationServerServlet):
class FederationStateV1Servlet(BaseFederationServerServlet):
PATH = "/state/(?P<room_id>[^/]*)/?"
+ CATEGORY = "Federation requests"
# This is when someone asks for all data for a given room.
async def on_GET(
@@ -170,6 +173,7 @@ class FederationStateV1Servlet(BaseFederationServerServlet):
class FederationStateIdsServlet(BaseFederationServerServlet):
PATH = "/state_ids/(?P<room_id>[^/]*)/?"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -187,6 +191,7 @@ class FederationStateIdsServlet(BaseFederationServerServlet):
class FederationBackfillServlet(BaseFederationServerServlet):
PATH = "/backfill/(?P<room_id>[^/]*)/?"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -225,6 +230,7 @@ class FederationTimestampLookupServlet(BaseFederationServerServlet):
"""
PATH = "/timestamp_to_event/(?P<room_id>[^/]*)/?"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -246,6 +252,7 @@ class FederationTimestampLookupServlet(BaseFederationServerServlet):
class FederationQueryServlet(BaseFederationServerServlet):
PATH = "/query/(?P<query_type>[^/]*)"
+ CATEGORY = "Federation requests"
# This is when we receive a server-server Query
async def on_GET(
@@ -262,6 +269,7 @@ class FederationQueryServlet(BaseFederationServerServlet):
class FederationMakeJoinServlet(BaseFederationServerServlet):
PATH = "/make_join/(?P<room_id>[^/]*)/(?P<user_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -297,6 +305,7 @@ class FederationMakeJoinServlet(BaseFederationServerServlet):
class FederationMakeLeaveServlet(BaseFederationServerServlet):
PATH = "/make_leave/(?P<room_id>[^/]*)/(?P<user_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -312,6 +321,7 @@ class FederationMakeLeaveServlet(BaseFederationServerServlet):
class FederationV1SendLeaveServlet(BaseFederationServerServlet):
PATH = "/send_leave/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_PUT(
self,
@@ -327,6 +337,7 @@ class FederationV1SendLeaveServlet(BaseFederationServerServlet):
class FederationV2SendLeaveServlet(BaseFederationServerServlet):
PATH = "/send_leave/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
PREFIX = FEDERATION_V2_PREFIX
@@ -344,6 +355,7 @@ class FederationV2SendLeaveServlet(BaseFederationServerServlet):
class FederationMakeKnockServlet(BaseFederationServerServlet):
PATH = "/make_knock/(?P<room_id>[^/]*)/(?P<user_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -366,6 +378,7 @@ class FederationMakeKnockServlet(BaseFederationServerServlet):
class FederationV1SendKnockServlet(BaseFederationServerServlet):
PATH = "/send_knock/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_PUT(
self,
@@ -381,6 +394,7 @@ class FederationV1SendKnockServlet(BaseFederationServerServlet):
class FederationEventAuthServlet(BaseFederationServerServlet):
PATH = "/event_auth/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -395,6 +409,7 @@ class FederationEventAuthServlet(BaseFederationServerServlet):
class FederationV1SendJoinServlet(BaseFederationServerServlet):
PATH = "/send_join/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_PUT(
self,
@@ -412,6 +427,7 @@ class FederationV1SendJoinServlet(BaseFederationServerServlet):
class FederationV2SendJoinServlet(BaseFederationServerServlet):
PATH = "/send_join/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
PREFIX = FEDERATION_V2_PREFIX
@@ -455,6 +471,7 @@ class FederationV2SendJoinServlet(BaseFederationServerServlet):
class FederationV1InviteServlet(BaseFederationServerServlet):
PATH = "/invite/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_PUT(
self,
@@ -479,6 +496,7 @@ class FederationV1InviteServlet(BaseFederationServerServlet):
class FederationV2InviteServlet(BaseFederationServerServlet):
PATH = "/invite/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
+ CATEGORY = "Federation requests"
PREFIX = FEDERATION_V2_PREFIX
@@ -515,6 +533,7 @@ class FederationV2InviteServlet(BaseFederationServerServlet):
class FederationThirdPartyInviteExchangeServlet(BaseFederationServerServlet):
PATH = "/exchange_third_party_invite/(?P<room_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_PUT(
self,
@@ -529,6 +548,7 @@ class FederationThirdPartyInviteExchangeServlet(BaseFederationServerServlet):
class FederationClientKeysQueryServlet(BaseFederationServerServlet):
PATH = "/user/keys/query"
+ CATEGORY = "Federation requests"
async def on_POST(
self, origin: str, content: JsonDict, query: Dict[bytes, List[bytes]]
@@ -538,6 +558,7 @@ class FederationClientKeysQueryServlet(BaseFederationServerServlet):
class FederationUserDevicesQueryServlet(BaseFederationServerServlet):
PATH = "/user/devices/(?P<user_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_GET(
self,
@@ -551,6 +572,7 @@ class FederationUserDevicesQueryServlet(BaseFederationServerServlet):
class FederationClientKeysClaimServlet(BaseFederationServerServlet):
PATH = "/user/keys/claim"
+ CATEGORY = "Federation requests"
async def on_POST(
self, origin: str, content: JsonDict, query: Dict[bytes, List[bytes]]
@@ -561,6 +583,7 @@ class FederationClientKeysClaimServlet(BaseFederationServerServlet):
class FederationGetMissingEventsServlet(BaseFederationServerServlet):
PATH = "/get_missing_events/(?P<room_id>[^/]*)"
+ CATEGORY = "Federation requests"
async def on_POST(
self,
@@ -586,6 +609,7 @@ class FederationGetMissingEventsServlet(BaseFederationServerServlet):
class On3pidBindServlet(BaseFederationServerServlet):
PATH = "/3pid/onbind"
+ CATEGORY = "Federation requests"
REQUIRE_AUTH = False
@@ -618,6 +642,7 @@ class On3pidBindServlet(BaseFederationServerServlet):
class FederationVersionServlet(BaseFederationServlet):
PATH = "/version"
+ CATEGORY = "Federation requests"
REQUIRE_AUTH = False
@@ -640,6 +665,7 @@ class FederationVersionServlet(BaseFederationServlet):
class FederationRoomHierarchyServlet(BaseFederationServlet):
PATH = "/hierarchy/(?P<room_id>[^/]*)"
+ CATEGORY = "Federation requests"
def __init__(
self,
@@ -672,6 +698,7 @@ class RoomComplexityServlet(BaseFederationServlet):
PATH = "/rooms/(?P<room_id>[^/]*)/complexity"
PREFIX = FEDERATION_UNSTABLE_PREFIX
+ CATEGORY = "Federation requests (unstable)"
def __init__(
self,
diff --git a/synapse/rest/client/_base.py b/synapse/rest/client/_base.py
index b4cb90cb76..5c1c19e1f3 100644
--- a/synapse/rest/client/_base.py
+++ b/synapse/rest/client/_base.py
@@ -43,19 +43,22 @@ def client_patterns(
Returns:
An iterable of patterns.
"""
- patterns = []
+ versions = []
- if unstable:
- unstable_prefix = CLIENT_API_PREFIX + "/unstable"
- patterns.append(re.compile("^" + unstable_prefix + path_regex))
if v1:
- v1_prefix = CLIENT_API_PREFIX + "/api/v1"
- patterns.append(re.compile("^" + v1_prefix + path_regex))
- for release in releases:
- new_prefix = CLIENT_API_PREFIX + f"/{release}"
- patterns.append(re.compile("^" + new_prefix + path_regex))
+ versions.append("api/v1")
+ versions.extend(releases)
+ if unstable:
+ versions.append("unstable")
+
+ if len(versions) == 1:
+ versions_str = versions[0]
+ elif len(versions) > 1:
+ versions_str = "(" + "|".join(versions) + ")"
+ else:
+ raise RuntimeError("Must have at least one version for a URL")
- return patterns
+ return [re.compile("^" + CLIENT_API_PREFIX + "/" + versions_str + path_regex)]
def set_timeline_upper_limit(filter_json: JsonDict, filter_timeline_limit: int) -> None:
diff --git a/synapse/rest/client/account.py b/synapse/rest/client/account.py
index 484d7440a4..3d0c55daa0 100644
--- a/synapse/rest/client/account.py
+++ b/synapse/rest/client/account.py
@@ -576,6 +576,9 @@ class AddThreepidMsisdnSubmitTokenServlet(RestServlet):
class ThreepidRestServlet(RestServlet):
PATTERNS = client_patterns("/account/3pid$")
+ # This is used as a proxy for all the 3pid endpoints.
+
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -834,6 +837,7 @@ def assert_valid_next_link(hs: "HomeServer", next_link: str) -> None:
class WhoamiRestServlet(RestServlet):
PATTERNS = client_patterns("/account/whoami$")
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/account_data.py b/synapse/rest/client/account_data.py
index e805196fec..43193ad086 100644
--- a/synapse/rest/client/account_data.py
+++ b/synapse/rest/client/account_data.py
@@ -38,6 +38,7 @@ class AccountDataServlet(RestServlet):
PATTERNS = client_patterns(
"/user/(?P<user_id>[^/]*)/account_data/(?P<account_data_type>[^/]*)"
)
+ CATEGORY = "Account data requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -136,6 +137,7 @@ class RoomAccountDataServlet(RestServlet):
"/rooms/(?P<room_id>[^/]*)"
"/account_data/(?P<account_data_type>[^/]*)"
)
+ CATEGORY = "Account data requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/devices.py b/synapse/rest/client/devices.py
index dab4a77f7e..e97d0bf475 100644
--- a/synapse/rest/client/devices.py
+++ b/synapse/rest/client/devices.py
@@ -40,6 +40,7 @@ logger = logging.getLogger(__name__)
class DevicesRestServlet(RestServlet):
PATTERNS = client_patterns("/devices$")
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -123,6 +124,7 @@ class DeleteDevicesRestServlet(RestServlet):
class DeviceRestServlet(RestServlet):
PATTERNS = client_patterns("/devices/(?P<device_id>[^/]*)$")
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/events.py b/synapse/rest/client/events.py
index 694d77d287..3eca4fe21f 100644
--- a/synapse/rest/client/events.py
+++ b/synapse/rest/client/events.py
@@ -33,6 +33,7 @@ logger = logging.getLogger(__name__)
class EventStreamRestServlet(RestServlet):
PATTERNS = client_patterns("/events$", v1=True)
+ CATEGORY = "Sync requests"
DEFAULT_LONGPOLL_TIME_MS = 30000
@@ -76,6 +77,7 @@ class EventStreamRestServlet(RestServlet):
class EventRestServlet(RestServlet):
PATTERNS = client_patterns("/events/(?P<event_id>[^/]*)$", v1=True)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/filter.py b/synapse/rest/client/filter.py
index 236199897c..ab7d8c9419 100644
--- a/synapse/rest/client/filter.py
+++ b/synapse/rest/client/filter.py
@@ -31,6 +31,7 @@ logger = logging.getLogger(__name__)
class GetFilterRestServlet(RestServlet):
PATTERNS = client_patterns("/user/(?P<user_id>[^/]*)/filter/(?P<filter_id>[^/]*)")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -69,6 +70,7 @@ class GetFilterRestServlet(RestServlet):
class CreateFilterRestServlet(RestServlet):
PATTERNS = client_patterns("/user/(?P<user_id>[^/]*)/filter")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/initial_sync.py b/synapse/rest/client/initial_sync.py
index 9b1bb8b521..046a4364f2 100644
--- a/synapse/rest/client/initial_sync.py
+++ b/synapse/rest/client/initial_sync.py
@@ -28,6 +28,7 @@ if TYPE_CHECKING:
# TODO: Needs unit testing
class InitialSyncRestServlet(RestServlet):
PATTERNS = client_patterns("/initialSync$", v1=True)
+ CATEGORY = "Sync requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/keys.py b/synapse/rest/client/keys.py
index 32bb8b9a91..6209b79b01 100644
--- a/synapse/rest/client/keys.py
+++ b/synapse/rest/client/keys.py
@@ -89,6 +89,7 @@ class KeyUploadServlet(RestServlet):
"""
PATTERNS = client_patterns("/keys/upload(/(?P<device_id>[^/]+))?$")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -182,6 +183,7 @@ class KeyQueryServlet(RestServlet):
"""
PATTERNS = client_patterns("/keys/query$")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -225,6 +227,7 @@ class KeyChangesServlet(RestServlet):
"""
PATTERNS = client_patterns("/keys/changes$")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -274,6 +277,7 @@ class OneTimeKeyServlet(RestServlet):
"""
PATTERNS = client_patterns("/keys/claim$")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/knock.py b/synapse/rest/client/knock.py
index 4fa66904ba..0dc7960872 100644
--- a/synapse/rest/client/knock.py
+++ b/synapse/rest/client/knock.py
@@ -40,6 +40,7 @@ class KnockRoomAliasServlet(RestServlet):
"""
PATTERNS = client_patterns("/knock/(?P<room_identifier>[^/]*)")
+ CATEGORY = "Event sending requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/login.py b/synapse/rest/client/login.py
index 8adced41e5..b7e9c8f6b5 100644
--- a/synapse/rest/client/login.py
+++ b/synapse/rest/client/login.py
@@ -72,6 +72,8 @@ class LoginResponse(TypedDict, total=False):
class LoginRestServlet(RestServlet):
PATTERNS = client_patterns("/login$", v1=True)
+ CATEGORY = "Registration/login requests"
+
CAS_TYPE = "m.login.cas"
SSO_TYPE = "m.login.sso"
TOKEN_TYPE = "m.login.token"
@@ -537,6 +539,7 @@ def _get_auth_flow_dict_for_idp(idp: SsoIdentityProvider) -> JsonDict:
class RefreshTokenServlet(RestServlet):
PATTERNS = client_patterns("/refresh$")
+ CATEGORY = "Registration/login requests"
def __init__(self, hs: "HomeServer"):
self._auth_handler = hs.get_auth_handler()
@@ -590,6 +593,7 @@ class SsoRedirectServlet(RestServlet):
+ "/(r0|v3)/login/sso/redirect/(?P<idp_id>[A-Za-z0-9_.~-]+)$"
)
]
+ CATEGORY = "SSO requests needed for all SSO providers"
def __init__(self, hs: "HomeServer"):
# make sure that the relevant handlers are instantiated, so that they
diff --git a/synapse/rest/client/presence.py b/synapse/rest/client/presence.py
index 94dd4fe2f4..8e193330f8 100644
--- a/synapse/rest/client/presence.py
+++ b/synapse/rest/client/presence.py
@@ -33,6 +33,7 @@ logger = logging.getLogger(__name__)
class PresenceStatusRestServlet(RestServlet):
PATTERNS = client_patterns("/presence/(?P<user_id>[^/]*)/status", v1=True)
+ CATEGORY = "Presence requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/profile.py b/synapse/rest/client/profile.py
index e69fa0829d..493e1acea0 100644
--- a/synapse/rest/client/profile.py
+++ b/synapse/rest/client/profile.py
@@ -29,6 +29,7 @@ if TYPE_CHECKING:
class ProfileDisplaynameRestServlet(RestServlet):
PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/displayname", v1=True)
+ CATEGORY = "Event sending requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -86,6 +87,7 @@ class ProfileDisplaynameRestServlet(RestServlet):
class ProfileAvatarURLRestServlet(RestServlet):
PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/avatar_url", v1=True)
+ CATEGORY = "Event sending requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -142,6 +144,7 @@ class ProfileAvatarURLRestServlet(RestServlet):
class ProfileRestServlet(RestServlet):
PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)", v1=True)
+ CATEGORY = "Event sending requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/push_rule.py b/synapse/rest/client/push_rule.py
index ad5c10c99d..1147b6f8ec 100644
--- a/synapse/rest/client/push_rule.py
+++ b/synapse/rest/client/push_rule.py
@@ -44,6 +44,9 @@ class PushRuleRestServlet(RestServlet):
"Unrecognised request: You probably wanted a trailing slash"
)
+ WORKERS_DENIED_METHODS = ["PUT", "DELETE"]
+ CATEGORY = "Push rule requests"
+
def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
diff --git a/synapse/rest/client/read_marker.py b/synapse/rest/client/read_marker.py
index 852838515c..4f96e51eeb 100644
--- a/synapse/rest/client/read_marker.py
+++ b/synapse/rest/client/read_marker.py
@@ -31,6 +31,7 @@ logger = logging.getLogger(__name__)
class ReadMarkerRestServlet(RestServlet):
PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/read_markers$")
+ CATEGORY = "Receipts requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/receipts.py b/synapse/rest/client/receipts.py
index 28b7d30ea8..316e7b9982 100644
--- a/synapse/rest/client/receipts.py
+++ b/synapse/rest/client/receipts.py
@@ -36,6 +36,7 @@ class ReceiptRestServlet(RestServlet):
"/receipt/(?P<receipt_type>[^/]*)"
"/(?P<event_id>[^/]*)$"
)
+ CATEGORY = "Receipts requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/register.py b/synapse/rest/client/register.py
index 4adb5271d2..7f84a17e29 100644
--- a/synapse/rest/client/register.py
+++ b/synapse/rest/client/register.py
@@ -367,6 +367,7 @@ class RegistrationTokenValidityRestServlet(RestServlet):
f"/register/{LoginType.REGISTRATION_TOKEN}/validity",
releases=("v1",),
)
+ CATEGORY = "Registration/login requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -395,6 +396,7 @@ class RegistrationTokenValidityRestServlet(RestServlet):
class RegisterRestServlet(RestServlet):
PATTERNS = client_patterns("/register$")
+ CATEGORY = "Registration/login requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/relations.py b/synapse/rest/client/relations.py
index 7456d6f507..b8b296bc0c 100644
--- a/synapse/rest/client/relations.py
+++ b/synapse/rest/client/relations.py
@@ -42,6 +42,7 @@ class RelationPaginationServlet(RestServlet):
"(/(?P<relation_type>[^/]*)(/(?P<event_type>[^/]*))?)?$",
releases=("v1",),
)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -84,6 +85,7 @@ class RelationPaginationServlet(RestServlet):
class ThreadsServlet(RestServlet):
PATTERNS = (re.compile("^/_matrix/client/v1/rooms/(?P<room_id>[^/]*)/threads"),)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/room.py b/synapse/rest/client/room.py
index 129b6fe6b0..c0705d4291 100644
--- a/synapse/rest/client/room.py
+++ b/synapse/rest/client/room.py
@@ -140,7 +140,7 @@ class TransactionRestServlet(RestServlet):
class RoomCreateRestServlet(TransactionRestServlet):
- # No PATTERN; we have custom dispatch rules here
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__(hs)
@@ -180,6 +180,8 @@ class RoomCreateRestServlet(TransactionRestServlet):
# TODO: Needs unit testing for generic events
class RoomStateEventRestServlet(RestServlet):
+ CATEGORY = "Event sending requests"
+
def __init__(self, hs: "HomeServer"):
super().__init__()
self.event_creation_handler = hs.get_event_creation_handler()
@@ -323,6 +325,8 @@ class RoomStateEventRestServlet(RestServlet):
# TODO: Needs unit testing for generic events + feedback
class RoomSendEventRestServlet(TransactionRestServlet):
+ CATEGORY = "Event sending requests"
+
def __init__(self, hs: "HomeServer"):
super().__init__(hs)
self.event_creation_handler = hs.get_event_creation_handler()
@@ -398,6 +402,8 @@ class RoomSendEventRestServlet(TransactionRestServlet):
# TODO: Needs unit testing for room ID + alias joins
class JoinRoomAliasServlet(ResolveRoomIdMixin, TransactionRestServlet):
+ CATEGORY = "Event sending requests"
+
def __init__(self, hs: "HomeServer"):
super().__init__(hs)
super(ResolveRoomIdMixin, self).__init__(hs) # ensure the Mixin is set up
@@ -460,6 +466,7 @@ class JoinRoomAliasServlet(ResolveRoomIdMixin, TransactionRestServlet):
# TODO: Needs unit testing
class PublicRoomListRestServlet(RestServlet):
PATTERNS = client_patterns("/publicRooms$", v1=True)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -578,6 +585,7 @@ class PublicRoomListRestServlet(RestServlet):
# TODO: Needs unit testing
class RoomMemberListRestServlet(RestServlet):
PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/members$", v1=True)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -633,6 +641,7 @@ class RoomMemberListRestServlet(RestServlet):
# 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)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -654,6 +663,10 @@ class JoinedRoomMemberListRestServlet(RestServlet):
# TODO: Needs better unit testing
class RoomMessageListRestServlet(RestServlet):
PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/messages$", v1=True)
+ # TODO The routing information should be exposed programatically.
+ # I want to do this but for now I felt bad about leaving this without
+ # at least a visible warning on it.
+ CATEGORY = "Client API requests (ALL FOR SAME ROOM MUST GO TO SAME WORKER)"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -720,6 +733,7 @@ class RoomMessageListRestServlet(RestServlet):
# TODO: Needs unit testing
class RoomStateRestServlet(RestServlet):
PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/state$", v1=True)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -742,6 +756,7 @@ class RoomStateRestServlet(RestServlet):
# TODO: Needs unit testing
class RoomInitialSyncRestServlet(RestServlet):
PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/initialSync$", v1=True)
+ CATEGORY = "Sync requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -766,6 +781,7 @@ class RoomEventServlet(RestServlet):
PATTERNS = client_patterns(
"/rooms/(?P<room_id>[^/]*)/event/(?P<event_id>[^/]*)$", v1=True
)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -858,6 +874,7 @@ class RoomEventContextServlet(RestServlet):
PATTERNS = client_patterns(
"/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$", v1=True
)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -958,6 +975,8 @@ class RoomForgetRestServlet(TransactionRestServlet):
# TODO: Needs unit testing
class RoomMembershipRestServlet(TransactionRestServlet):
+ CATEGORY = "Event sending requests"
+
def __init__(self, hs: "HomeServer"):
super().__init__(hs)
self.room_member_handler = hs.get_room_member_handler()
@@ -1071,6 +1090,8 @@ class RoomMembershipRestServlet(TransactionRestServlet):
class RoomRedactEventRestServlet(TransactionRestServlet):
+ CATEGORY = "Event sending requests"
+
def __init__(self, hs: "HomeServer"):
super().__init__(hs)
self.event_creation_handler = hs.get_event_creation_handler()
@@ -1164,6 +1185,7 @@ class RoomTypingRestServlet(RestServlet):
PATTERNS = client_patterns(
"/rooms/(?P<room_id>[^/]*)/typing/(?P<user_id>[^/]*)$", v1=True
)
+ CATEGORY = "The typing stream"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -1195,7 +1217,7 @@ class RoomTypingRestServlet(RestServlet):
# 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.
+ # Defer getting the typing handler since it will raise on WORKER_PATTERNS.
typing_handler = self.hs.get_typing_writer_handler()
try:
@@ -1224,6 +1246,7 @@ class RoomAliasListServlet(RestServlet):
r"/rooms/(?P<room_id>[^/]*)/aliases"
),
] + list(client_patterns("/rooms/(?P<room_id>[^/]*)/aliases$", unstable=False))
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -1244,6 +1267,7 @@ class RoomAliasListServlet(RestServlet):
class SearchRestServlet(RestServlet):
PATTERNS = client_patterns("/search$", v1=True)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -1263,6 +1287,7 @@ class SearchRestServlet(RestServlet):
class JoinedRoomsRestServlet(RestServlet):
PATTERNS = client_patterns("/joined_rooms$", v1=True)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -1334,6 +1359,7 @@ class TimestampLookupRestServlet(RestServlet):
PATTERNS = (
re.compile("^/_matrix/client/v1/rooms/(?P<room_id>[^/]*)/timestamp_to_event$"),
)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -1365,6 +1391,8 @@ class TimestampLookupRestServlet(RestServlet):
class RoomHierarchyRestServlet(RestServlet):
PATTERNS = (re.compile("^/_matrix/client/v1/rooms/(?P<room_id>[^/]*)/hierarchy$"),)
+ WORKERS = PATTERNS
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -1405,6 +1433,7 @@ class RoomSummaryRestServlet(ResolveRoomIdMixin, RestServlet):
"/rooms/(?P<room_identifier>[^/]*)/summary$"
),
)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__(hs)
diff --git a/synapse/rest/client/room_batch.py b/synapse/rest/client/room_batch.py
index ef284ecc11..69f85112d8 100644
--- a/synapse/rest/client/room_batch.py
+++ b/synapse/rest/client/room_batch.py
@@ -69,6 +69,7 @@ class RoomBatchSendEventRestServlet(RestServlet):
"/rooms/(?P<room_id>[^/]*)/batch_send$"
),
)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/room_keys.py b/synapse/rest/client/room_keys.py
index 4e7ffdb555..aad54f8c54 100644
--- a/synapse/rest/client/room_keys.py
+++ b/synapse/rest/client/room_keys.py
@@ -37,6 +37,7 @@ class RoomKeysServlet(RestServlet):
PATTERNS = client_patterns(
"/room_keys/keys(/(?P<room_id>[^/]+))?(/(?P<session_id>[^/]+))?$"
)
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -253,6 +254,7 @@ class RoomKeysServlet(RestServlet):
class RoomKeysNewVersionServlet(RestServlet):
PATTERNS = client_patterns("/room_keys/version$")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -328,6 +330,7 @@ class RoomKeysNewVersionServlet(RestServlet):
class RoomKeysVersionServlet(RestServlet):
PATTERNS = client_patterns("/room_keys/version/(?P<version>[^/]+)$")
+ CATEGORY = "Encryption requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/sendtodevice.py b/synapse/rest/client/sendtodevice.py
index 110af6df47..7dfa3a2596 100644
--- a/synapse/rest/client/sendtodevice.py
+++ b/synapse/rest/client/sendtodevice.py
@@ -35,6 +35,7 @@ class SendToDeviceRestServlet(servlet.RestServlet):
PATTERNS = client_patterns(
"/sendToDevice/(?P<message_type>[^/]*)/(?P<txn_id>[^/]*)$"
)
+ CATEGORY = "The to_device stream"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/sync.py b/synapse/rest/client/sync.py
index e578b26fa3..03b0578945 100644
--- a/synapse/rest/client/sync.py
+++ b/synapse/rest/client/sync.py
@@ -87,6 +87,7 @@ class SyncRestServlet(RestServlet):
PATTERNS = client_patterns("/sync$")
ALLOWED_PRESENCE = {"online", "offline", "unavailable"}
+ CATEGORY = "Sync requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/tags.py b/synapse/rest/client/tags.py
index dde08417a4..94bd51fe8f 100644
--- a/synapse/rest/client/tags.py
+++ b/synapse/rest/client/tags.py
@@ -37,6 +37,7 @@ class TagListServlet(RestServlet):
PATTERNS = client_patterns(
"/user/(?P<user_id>[^/]*)/rooms/(?P<room_id>[^/]*)/tags$"
)
+ CATEGORY = "Account data requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
@@ -64,6 +65,7 @@ class TagServlet(RestServlet):
PATTERNS = client_patterns(
"/user/(?P<user_id>[^/]*)/rooms/(?P<room_id>[^/]*)/tags/(?P<tag>[^/]*)"
)
+ CATEGORY = "Account data requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/user_directory.py b/synapse/rest/client/user_directory.py
index 4670fad608..5136497c77 100644
--- a/synapse/rest/client/user_directory.py
+++ b/synapse/rest/client/user_directory.py
@@ -31,6 +31,7 @@ logger = logging.getLogger(__name__)
class UserDirectorySearchRestServlet(RestServlet):
PATTERNS = client_patterns("/user_directory/search$")
+ CATEGORY = "User directory search requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py
index ec171582b3..59aed66464 100644
--- a/synapse/rest/client/versions.py
+++ b/synapse/rest/client/versions.py
@@ -34,6 +34,7 @@ logger = logging.getLogger(__name__)
class VersionsRestServlet(RestServlet):
PATTERNS = [re.compile("^/_matrix/client/versions$")]
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/client/voip.py b/synapse/rest/client/voip.py
index ea7e025156..133790c97c 100644
--- a/synapse/rest/client/voip.py
+++ b/synapse/rest/client/voip.py
@@ -29,6 +29,7 @@ if TYPE_CHECKING:
class VoipRestServlet(RestServlet):
PATTERNS = client_patterns("/voip/turnServer$", v1=True)
+ CATEGORY = "Client API requests"
def __init__(self, hs: "HomeServer"):
super().__init__()
diff --git a/synapse/rest/key/v2/remote_key_resource.py b/synapse/rest/key/v2/remote_key_resource.py
index 19820886f5..3bdb6ec909 100644
--- a/synapse/rest/key/v2/remote_key_resource.py
+++ b/synapse/rest/key/v2/remote_key_resource.py
@@ -93,6 +93,8 @@ class RemoteKey(RestServlet):
}
"""
+ CATEGORY = "Federation requests"
+
def __init__(self, hs: "HomeServer"):
self.fetcher = ServerKeyFetcher(hs)
self.store = hs.get_datastores().main
|