Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

MSC2197 Search Filters over Federation #5859

Merged
merged 5 commits into from
Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.d/5859.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add unstable support for MSC2197 (filtered search requests over federation), in
order to allow upcoming room directory query performance improvements.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want this on one line? I don't know if towncrier's formatter will handle multiline stuff sanely?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes please. Towncrier manages it ok, but github treats the newline as a hard break when it is C&Ped into the release notes, which doesn't look pretty.

46 changes: 31 additions & 15 deletions synapse/federation/transport/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,21 +327,37 @@ def get_public_rooms(
include_all_networks=False,
third_party_instance_id=None,
):
path = _create_v1_path("/publicRooms")

args = {"include_all_networks": "true" if include_all_networks else "false"}
if third_party_instance_id:
args["third_party_instance_id"] = (third_party_instance_id,)
if limit:
args["limit"] = [str(limit)]
if since_token:
args["since"] = [since_token]

# TODO(erikj): Actually send the search_filter across federation.

response = yield self.client.get_json(
destination=remote_server, path=path, args=args, ignore_backoff=True
)
if search_filter:
# TODO(MSC2197): Move to V1 prefix
path = _create_path(FEDERATION_UNSTABLE_PREFIX, "/publicRooms")

data = {"include_all_networks": "true" if include_all_networks else "false"}
if third_party_instance_id:
data["third_party_instance_id"] = third_party_instance_id
if limit:
data["limit"] = str(limit)
if since_token:
data["since"] = since_token

data["filter"] = search_filter

response = yield self.client.post_json(
destination=remote_server, path=path, data=data, ignore_backoff=True
)
else:
path = _create_v1_path("/publicRooms")

args = {"include_all_networks": "true" if include_all_networks else "false"}
if third_party_instance_id:
args["third_party_instance_id"] = (third_party_instance_id,)
if limit:
args["limit"] = [str(limit)]
if since_token:
args["since"] = [since_token]

response = yield self.client.get_json(
destination=remote_server, path=path, args=args, ignore_backoff=True
)

return response

Expand Down
60 changes: 59 additions & 1 deletion synapse/federation/transport/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,64 @@ async def on_GET(self, origin, content, query):
return 200, data


class UnstablePublicRoomList(BaseFederationServlet):
"""
Fetch the public room list for this server.

This API returns information in the same format as /publicRooms on the
client API, but will only ever include local public rooms and hence is
intended for consumption by other home servers.

This is the unstable-prefixed version which adds support for MSC2197, which
is still undergoing review.
"""

PATH = "/publicRooms"
PREFIX = FEDERATION_UNSTABLE_PREFIX

def __init__(self, handler, authenticator, ratelimiter, server_name, allow_access):
super(UnstablePublicRoomList, self).__init__(
handler, authenticator, ratelimiter, server_name
)
self.allow_access = allow_access

# TODO(MSC2197): Move away from Unstable prefix and back to normal prefix
async def on_POST(self, origin, content, query):
if not self.allow_access:
raise FederationDeniedError(origin)

limit = 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 search_filter is None:
logger.warning("Nonefilter")

data = await self.handler.get_local_public_room_list(
limit=limit,
since_token=since_token,
search_filter=search_filter,
network_tuple=network_tuple,
from_federation=True,
)

return 200, data


class FederationVersionServlet(BaseFederationServlet):
PATH = "/version"

Expand Down Expand Up @@ -1315,7 +1373,7 @@ async def on_GET(self, origin, content, query, room_id):

OPENID_SERVLET_CLASSES = (OpenIdUserInfo,)

ROOM_LIST_CLASSES = (PublicRoomList,)
ROOM_LIST_CLASSES = (PublicRoomList, UnstablePublicRoomList)

GROUP_SERVER_SERVLET_CLASSES = (
FederationGroupsProfileServlet,
Expand Down
29 changes: 28 additions & 1 deletion synapse/handlers/room_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from twisted.internet import defer

from synapse.api.constants import EventTypes, JoinRules
from synapse.api.errors import Codes, HttpResponseException
from synapse.types import ThirdPartyInstanceID
from synapse.util.async_helpers import concurrently_execute
from synapse.util.caches.descriptors import cachedInlineCallbacks
Expand Down Expand Up @@ -485,7 +486,33 @@ def get_remote_public_room_list(
return {"chunk": [], "total_room_count_estimate": 0}

if search_filter:
# We currently don't support searching across federation, so we have
# Searching across federation is defined in MSC2197.
# However, the remote homeserver may or may not actually support it.
# So we first try an MSC2197 remote-filtered search, then fall back
# to a locally-filtered search if we must.

try:
res = yield self._get_remote_list_cached(
server_name,
limit=limit,
since_token=since_token,
include_all_networks=include_all_networks,
third_party_instance_id=third_party_instance_id,
search_filter=search_filter,
)
return res
except HttpResponseException as hre:
syn_err = hre.to_synapse_error()
if hre.code in (404, 405) or syn_err.errcode in (
Codes.UNRECOGNIZED,
Codes.NOT_FOUND,
):
logger.debug("Falling back to locally-filtered /publicRooms")
else:
raise # Not an error that should trigger a fallback.

# if we reach this point, then we fall back to the situation where
# we currently don't support searching across federation, so we have
# to do it manually without pagination
limit = None
since_token = None
Expand Down