diff --git a/synapse/handlers/sliding_sync.py b/synapse/handlers/sliding_sync.py
index 1c2ed95660..b108e8a4a2 100644
--- a/synapse/handlers/sliding_sync.py
+++ b/synapse/handlers/sliding_sync.py
@@ -1807,7 +1807,7 @@ class SlidingSyncHandler:
"""
user_id = sync_config.user.to_string()
- device_id = sync_config.device_id
+ device_id = sync_config.requester.device_id
# Check that this request has a valid device ID (not all requests have
# to belong to a device, and so device_id is None), and that the
diff --git a/synapse/rest/client/sync.py b/synapse/rest/client/sync.py
index b3967db18d..2909052bbf 100644
--- a/synapse/rest/client/sync.py
+++ b/synapse/rest/client/sync.py
@@ -881,7 +881,6 @@ class SlidingSyncRestServlet(RestServlet):
)
user = requester.user
- device_id = requester.device_id
timeout = parse_integer(request, "timeout", default=0)
# Position in the stream
@@ -902,11 +901,12 @@ class SlidingSyncRestServlet(RestServlet):
sync_config = SlidingSyncConfig(
user=user,
- device_id=device_id,
+ requester=requester,
# FIXME: Currently, we're just manually copying the fields from the
- # `SlidingSyncBody` into the config. How can we gurantee into the future
+ # `SlidingSyncBody` into the config. How can we guarantee into the future
# that we don't forget any? I would like something more structured like
# `copy_attributes(from=body, to=config)`
+ conn_id=body.conn_id,
lists=body.lists,
room_subscriptions=body.room_subscriptions,
extensions=body.extensions,
diff --git a/synapse/types/handlers/__init__.py b/synapse/types/handlers/__init__.py
index 642e5483a9..0c2ab13c93 100644
--- a/synapse/types/handlers/__init__.py
+++ b/synapse/types/handlers/__init__.py
@@ -34,6 +34,7 @@ from synapse.events import EventBase
from synapse.types import (
JsonDict,
JsonMapping,
+ Requester,
SlidingSyncStreamToken,
StreamToken,
UserID,
@@ -108,7 +109,7 @@ class SlidingSyncConfig(SlidingSyncBody):
"""
user: UserID
- device_id: Optional[str]
+ requester: Requester
# Pydantic config
class Config:
@@ -119,6 +120,31 @@ class SlidingSyncConfig(SlidingSyncBody):
# Allow custom types like `UserID` to be used in the model
arbitrary_types_allowed = True
+ def connection_id(self) -> str:
+ """Return a string identifier for this connection. May clash with
+ connection IDs from different users.
+
+ This is generally a combination of device ID and conn_id. However, both
+ these two are optional (e.g. puppet access tokens don't have device
+ IDs), so this handles those edge cases.
+ """
+
+ # `conn_id` can be null, in which case we default to the empty string
+ # (if conn ID is empty then the client can't have multiple sync loops)
+ conn_id = self.conn_id or ""
+
+ if self.requester.device_id:
+ return f"D/{self.requester.device_id}/{conn_id}"
+
+ if self.requester.access_token_id:
+ # If we don't have a device, then the access token ID should be a
+ # stable ID.
+ return f"A/{self.requester.access_token_id}/{conn_id}"
+
+ # If we have neither then its likely an AS or some weird token. Either
+ # way we can just fail here.
+ raise Exception("Cannot use sliding sync with access token type")
+
class OperationType(Enum):
"""
diff --git a/synapse/types/rest/client/__init__.py b/synapse/types/rest/client/__init__.py
index dbe37bc712..5be8cf5389 100644
--- a/synapse/types/rest/client/__init__.py
+++ b/synapse/types/rest/client/__init__.py
@@ -120,6 +120,9 @@ class SlidingSyncBody(RequestBodyModel):
Sliding Sync API request body.
Attributes:
+ conn_id: An optional string to identify this connection to the server. If this
+ is missing, only 1 sliding sync connection can be made to the server at
+ any one time.
lists: Sliding window API. A map of list key to list information
(:class:`SlidingSyncList`). Max lists: 100. The list keys should be
arbitrary strings which the client is using to refer to the list. Keep this
@@ -315,6 +318,8 @@ class SlidingSyncBody(RequestBodyModel):
to_device: Optional[ToDeviceExtension] = None
+ conn_id: Optional[str]
+
# mypy workaround via https://github.com/pydantic/pydantic/issues/156#issuecomment-1130883884
if TYPE_CHECKING:
lists: Optional[Dict[str, SlidingSyncList]] = None
|